From 5665ea498b5e66d9cf64d5fe5a88aac5cfff35ab Mon Sep 17 00:00:00 2001
From: Storm21 <61840624+Storm21CH@users.noreply.github.com>
Date: Sat, 30 Jan 2021 16:48:50 +0100
Subject: [PATCH] Add files via upload
---
bdk/libs/compr/blz.c | 98 +
bdk/libs/compr/blz.h | 36 +
bdk/libs/compr/lz.c | 181 +
bdk/libs/compr/lz.h | 52 +
bdk/libs/compr/lz4.c | 1672 ++++++++
bdk/libs/compr/lz4.h | 569 +++
bdk/libs/fatfs/diskio.h | 88 +
bdk/libs/fatfs/ff.c | 6861 ++++++++++++++++++++++++++++++++
bdk/libs/fatfs/ff.h | 390 ++
bdk/libs/fatfs/ffunicode.c | 625 +++
bdk/libs/lv_conf.h | 384 ++
bdk/thermal/fan.c | 106 +
bdk/thermal/fan.h | 29 +
bdk/thermal/tmp451.c | 87 +
bdk/thermal/tmp451.h | 46 +
bdk/usb/usb_descriptor_types.h | 238 ++
bdk/usb/usb_descriptors.c | 558 +++
bdk/usb/usb_gadget_hid.c | 441 ++
bdk/usb/usb_gadget_ums.c | 1925 +++++++++
bdk/usb/usb_t210.h | 293 ++
bdk/usb/usbd.c | 1595 ++++++++
bdk/usb/usbd.h | 203 +
bdk/usb/xusbd.c | 2026 ++++++++++
bdk/utils/aarch64_util.h | 37 +
bdk/utils/btn.c | 114 +
bdk/utils/btn.h | 34 +
bdk/utils/dirlist.c | 102 +
bdk/utils/dirlist.h | 19 +
bdk/utils/ini.c | 189 +
bdk/utils/ini.h | 50 +
bdk/utils/list.h | 97 +
bdk/utils/sprintf.c | 142 +
bdk/utils/sprintf.h | 24 +
bdk/utils/types.h | 128 +
bdk/utils/util.c | 242 ++
bdk/utils/util.h | 103 +
36 files changed, 19784 insertions(+)
create mode 100644 bdk/libs/compr/blz.c
create mode 100644 bdk/libs/compr/blz.h
create mode 100644 bdk/libs/compr/lz.c
create mode 100644 bdk/libs/compr/lz.h
create mode 100644 bdk/libs/compr/lz4.c
create mode 100644 bdk/libs/compr/lz4.h
create mode 100644 bdk/libs/fatfs/diskio.h
create mode 100644 bdk/libs/fatfs/ff.c
create mode 100644 bdk/libs/fatfs/ff.h
create mode 100644 bdk/libs/fatfs/ffunicode.c
create mode 100644 bdk/libs/lv_conf.h
create mode 100644 bdk/thermal/fan.c
create mode 100644 bdk/thermal/fan.h
create mode 100644 bdk/thermal/tmp451.c
create mode 100644 bdk/thermal/tmp451.h
create mode 100644 bdk/usb/usb_descriptor_types.h
create mode 100644 bdk/usb/usb_descriptors.c
create mode 100644 bdk/usb/usb_gadget_hid.c
create mode 100644 bdk/usb/usb_gadget_ums.c
create mode 100644 bdk/usb/usb_t210.h
create mode 100644 bdk/usb/usbd.c
create mode 100644 bdk/usb/usbd.h
create mode 100644 bdk/usb/xusbd.c
create mode 100644 bdk/utils/aarch64_util.h
create mode 100644 bdk/utils/btn.c
create mode 100644 bdk/utils/btn.h
create mode 100644 bdk/utils/dirlist.c
create mode 100644 bdk/utils/dirlist.h
create mode 100644 bdk/utils/ini.c
create mode 100644 bdk/utils/ini.h
create mode 100644 bdk/utils/list.h
create mode 100644 bdk/utils/sprintf.c
create mode 100644 bdk/utils/sprintf.h
create mode 100644 bdk/utils/types.h
create mode 100644 bdk/utils/util.c
create mode 100644 bdk/utils/util.h
diff --git a/bdk/libs/compr/blz.c b/bdk/libs/compr/blz.c
new file mode 100644
index 00000000..685ef4af
--- /dev/null
+++ b/bdk/libs/compr/blz.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018 rajkosto
+ * Copyright (c) 2018 SciresM
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "blz.h"
+
+const blz_footer *blz_get_footer(const unsigned char *compData, unsigned int compDataLen, blz_footer *outFooter)
+{
+ if (compDataLen < sizeof(blz_footer))
+ return NULL;
+
+ const blz_footer *srcFooter = (const blz_footer*)&compData[compDataLen - sizeof(blz_footer)];
+ if (outFooter != NULL)
+ memcpy(outFooter, srcFooter, sizeof(blz_footer)); // Must be a memcpy because no umaligned accesses on ARMv4.
+
+ return srcFooter;
+}
+
+// From https://github.com/SciresM/hactool/blob/master/kip.c which is exactly how kernel does it, thanks SciresM!
+int blz_uncompress_inplace(unsigned char *dataBuf, unsigned int compSize, const blz_footer *footer)
+{
+ u32 addl_size = footer->addl_size;
+ u32 header_size = footer->header_size;
+ u32 cmp_and_hdr_size = footer->cmp_and_hdr_size;
+
+ unsigned char* cmp_start = &dataBuf[compSize] - cmp_and_hdr_size;
+ u32 cmp_ofs = cmp_and_hdr_size - header_size;
+ u32 out_ofs = cmp_and_hdr_size + addl_size;
+
+ while (out_ofs)
+ {
+ unsigned char control = cmp_start[--cmp_ofs];
+ for (unsigned int i=0; i<8; i++)
+ {
+ if (control & 0x80)
+ {
+ if (cmp_ofs < 2)
+ return 0; // Out of bounds.
+
+ cmp_ofs -= 2;
+ u16 seg_val = ((unsigned int)(cmp_start[cmp_ofs + 1]) << 8) | cmp_start[cmp_ofs];
+ u32 seg_size = ((seg_val >> 12) & 0xF) + 3;
+ u32 seg_ofs = (seg_val & 0x0FFF) + 3;
+ if (out_ofs < seg_size) // Kernel restricts segment copy to stay in bounds.
+ seg_size = out_ofs;
+
+ out_ofs -= seg_size;
+
+ for (unsigned int j = 0; j < seg_size; j++)
+ cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
+ }
+ else
+ {
+ // Copy directly.
+ if (cmp_ofs < 1)
+ return 0; //out of bounds
+
+ cmp_start[--out_ofs] = cmp_start[--cmp_ofs];
+ }
+ control <<= 1;
+ if (out_ofs == 0) // Blz works backwards, so if it reaches byte 0, it's done.
+ return 1;
+ }
+ }
+
+ return 1;
+}
+
+int blz_uncompress_srcdest(const unsigned char *compData, unsigned int compDataLen, unsigned char *dstData, unsigned int dstSize)
+{
+ blz_footer footer;
+ const blz_footer *compFooterPtr = blz_get_footer(compData, compDataLen, &footer);
+ if (compFooterPtr == NULL)
+ return 0;
+
+ // Decompression must be done in-place, so need to copy the relevant compressed data first.
+ unsigned int numCompBytes = (const unsigned char*)(compFooterPtr)-compData;
+ memcpy(dstData, compData, numCompBytes);
+ memset(&dstData[numCompBytes], 0, dstSize - numCompBytes);
+
+ return blz_uncompress_inplace(dstData, compDataLen, &footer);
+}
diff --git a/bdk/libs/compr/blz.h b/bdk/libs/compr/blz.h
new file mode 100644
index 00000000..a1cce376
--- /dev/null
+++ b/bdk/libs/compr/blz.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 rajkosto
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _BLZ_H_
+#define _BLZ_H_
+
+#include
+
+typedef struct _blz_footer
+{
+ u32 cmp_and_hdr_size;
+ u32 header_size;
+ u32 addl_size;
+} blz_footer;
+
+// Returns pointer to footer in compData if present, additionally copies it to outFooter if not NULL.
+const blz_footer *blz_get_footer(const unsigned char *compData, unsigned int compDataLen, blz_footer *outFooter);
+// Returns 0 on failure.
+int blz_uncompress_inplace(unsigned char *dataBuf, unsigned int compSize, const blz_footer *footer);
+// Returns 0 on failure.
+int blz_uncompress_srcdest(const unsigned char *compData, unsigned int compDataLen, unsigned char *dstData, unsigned int dstSize);
+
+#endif
diff --git a/bdk/libs/compr/lz.c b/bdk/libs/compr/lz.c
new file mode 100644
index 00000000..94b64c6f
--- /dev/null
+++ b/bdk/libs/compr/lz.c
@@ -0,0 +1,181 @@
+/*************************************************************************
+* Name: lz.c
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder implementation.
+* Reentrant: Yes
+*
+* The LZ77 compression scheme is a substitutional compression scheme
+* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in
+* its design, and uses no fancy bit level compression.
+*
+* This is my first attempt at an implementation of a LZ77 code/decoder.
+*
+* The principle of the LZ77 compression algorithm is to store repeated
+* occurrences of strings as references to previous occurrences of the same
+* string. The point is that the reference consumes less space than the
+* string itself, provided that the string is long enough (in this
+* implementation, the string has to be at least 4 bytes long, since the
+* minimum coded reference is 3 bytes long). Also note that the term
+* "string" refers to any kind of byte sequence (it does not have to be
+* an ASCII string, for instance).
+*
+* The coder uses a brute force approach to finding string matches in the
+* history buffer (or "sliding window", if you wish), which is very, very
+* slow. I recon the complexity is somewhere between O(n^2) and O(n^3),
+* depending on the input data.
+*
+* There is also a faster implementation that uses a large working buffer
+* in which a "jump table" is stored, which is used to quickly find
+* possible string matches (see the source code for LZ_CompressFast() for
+* more information). The faster method is an order of magnitude faster,
+* but still quite slow compared to other compression methods.
+*
+* The upside is that decompression is very fast, and the compression ratio
+* is often very good.
+*
+* The reference to a string is coded as a (length,offset) pair, where the
+* length indicates the length of the string, and the offset gives the
+* offset from the current data position. To distinguish between string
+* references and literal strings (uncompressed bytes), a string reference
+* is preceded by a marker byte, which is chosen as the least common byte
+* symbol in the input data stream (this marker byte is stored in the
+* output stream as the first byte).
+*
+* Occurrences of the marker byte in the stream are encoded as the marker
+* byte followed by a zero byte, which means that occurrences of the marker
+* byte have to be coded with two bytes.
+*
+* The lengths and offsets are coded in a variable length fashion, allowing
+* values of any magnitude (up to 4294967295 in this implementation).
+*
+* With this compression scheme, the worst case compression result is
+* (257/256)*insize + 1.
+*
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2006 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+
+/*************************************************************************
+* INTERNAL FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* _LZ_ReadVarSize() - Read unsigned integer with variable number of
+* bytes depending on value.
+*************************************************************************/
+
+static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
+{
+ unsigned int y, b, num_bytes;
+
+ /* Read complete value (stop when byte contains zero in 8:th bit) */
+ y = 0;
+ num_bytes = 0;
+ do
+ {
+ b = (unsigned int) (*buf ++);
+ y = (y << 7) | (b & 0x0000007f);
+ ++ num_bytes;
+ }
+ while( b & 0x00000080 );
+
+ /* Store value in x */
+ *x = y;
+
+ /* Return number of bytes read */
+ return num_bytes;
+}
+
+
+
+/*************************************************************************
+* PUBLIC FUNCTIONS *
+*************************************************************************/
+
+
+/*************************************************************************
+* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
+* in - Input (compressed) buffer.
+* out - Output (uncompressed) buffer. This buffer must be large
+* enough to hold the uncompressed data.
+* insize - Number of input bytes.
+*************************************************************************/
+
+unsigned int LZ_Uncompress( const unsigned char *in, unsigned char *out,
+ unsigned int insize )
+{
+ unsigned char marker, symbol;
+ unsigned int i, inpos, outpos, length, offset;
+
+ /* Do we have anything to uncompress? */
+ if( insize < 1 )
+ {
+ return 0;
+ }
+
+ /* Get marker symbol from input stream */
+ marker = in[ 0 ];
+ inpos = 1;
+
+ /* Main decompression loop */
+ outpos = 0;
+ do
+ {
+ symbol = in[ inpos ++ ];
+ if( symbol == marker )
+ {
+ /* We had a marker byte */
+ if( in[ inpos ] == 0 )
+ {
+ /* It was a single occurrence of the marker byte */
+ out[ outpos ++ ] = marker;
+ ++ inpos;
+ }
+ else
+ {
+ /* Extract true length and offset */
+ inpos += _LZ_ReadVarSize( &length, &in[ inpos ] );
+ inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] );
+
+ /* Copy corresponding data from history window */
+ for( i = 0; i < length; ++ i )
+ {
+ out[ outpos ] = out[ outpos - offset ];
+ ++ outpos;
+ }
+ }
+ }
+ else
+ {
+ /* No marker, plain copy */
+ out[ outpos ++ ] = symbol;
+ }
+ }
+ while( inpos < insize );
+
+ return outpos;
+}
diff --git a/bdk/libs/compr/lz.h b/bdk/libs/compr/lz.h
new file mode 100644
index 00000000..ef670553
--- /dev/null
+++ b/bdk/libs/compr/lz.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+* Name: lz.h
+* Author: Marcus Geelnard
+* Description: LZ77 coder/decoder interface.
+* Reentrant: Yes
+*-------------------------------------------------------------------------
+* Copyright (c) 2003-2006 Marcus Geelnard
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would
+* be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not
+* be misrepresented as being the original software.
+*
+* 3. This notice may not be removed or altered from any source
+* distribution.
+*
+* Marcus Geelnard
+* marcus.geelnard at home.se
+*************************************************************************/
+
+#ifndef _lz_h_
+#define _lz_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+* Function prototypes
+*************************************************************************/
+
+unsigned int LZ_Uncompress( const unsigned char *in, unsigned char *out,
+ unsigned int insize );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _lz_h_ */
diff --git a/bdk/libs/compr/lz4.c b/bdk/libs/compr/lz4.c
new file mode 100644
index 00000000..ddea3d80
--- /dev/null
+++ b/bdk/libs/compr/lz4.c
@@ -0,0 +1,1672 @@
+/*
+ LZ4 - Fast LZ compression algorithm
+ Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+
+/*-************************************
+* Tuning parameters
+**************************************/
+/*
+ * ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define ACCELERATION_DEFAULT 1
+
+
+/*-************************************
+* Dependency
+**************************************/
+#define LZ4_STATIC_LINKING_ONLY
+#include "lz4.h"
+/* see also "memory routines" below */
+
+
+/*-************************************
+* Compiler Options
+**************************************/
+#ifndef LZ4_FORCE_INLINE
+# ifdef _MSC_VER /* Visual Studio */
+# define LZ4_FORCE_INLINE static __forceinline
+# else
+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# ifdef __GNUC__
+# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
+# else
+# define LZ4_FORCE_INLINE static inline
+# endif
+# else
+# define LZ4_FORCE_INLINE static
+# endif /* __STDC_VERSION__ */
+# endif /* _MSC_VER */
+#endif /* LZ4_FORCE_INLINE */
+
+/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+ * Gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy,
+ * together with a simple 8-byte copy loop as a fall-back path.
+ * However, this optimization hurts the decompression speed by >30%,
+ * because the execution does not go to the optimized loop
+ * for typical compressible data, and all of the preamble checks
+ * before going to the fall-back path become useless overhead.
+ * This optimization happens only with the -O3 flag, and -O2 generates
+ * a simple 8-byte copy loop.
+ * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy
+ * functions are annotated with __attribute__((optimize("O2"))),
+ * and also LZ4_wildCopy is forcibly inlined, so that the O2 attribute
+ * of LZ4_wildCopy does not affect the compression speed.
+ */
+#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__)
+# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2")))
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE
+#else
+# define LZ4_FORCE_O2_GCC_PPC64LE
+# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+# define expect(expr,value) (__builtin_expect ((expr),(value)) )
+#else
+# define expect(expr,value) (expr)
+#endif
+
+#define likely(expr) expect((expr) != 0, 1)
+#define unlikely(expr) expect((expr) != 0, 0)
+
+
+/*-************************************
+* Memory routines
+**************************************/
+#include /* malloc, calloc, free */
+#define ALLOC(s) malloc(s)
+#define ALLOC_AND_ZERO(s) calloc(1,s)
+#define FREEMEM free
+#include /* memset, memcpy */
+#define MEM_INIT memset
+
+
+/*-************************************
+* Basic Types
+**************************************/
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+typedef uintptr_t uptrval;
+typedef size_t reg_t; /* 32-bits in x32 mode */
+
+/*-************************************
+* Reading and writing into memory
+**************************************/
+static unsigned LZ4_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+static U16 LZ4_read16(const void* memPtr)
+{
+ U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static U32 LZ4_read32(const void* memPtr)
+{
+ U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static reg_t LZ4_read_ARCH(const void* memPtr)
+{
+ reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static void LZ4_write16(void* memPtr, U16 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static void LZ4_write32(void* memPtr, U32 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static U16 LZ4_readLE16(const void* memPtr)
+{
+ if (LZ4_isLittleEndian()) {
+ return LZ4_read16(memPtr);
+ } else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)((U16)p[0] + (p[1]<<8));
+ }
+}
+
+static void LZ4_writeLE16(void* memPtr, U16 value)
+{
+ if (LZ4_isLittleEndian()) {
+ LZ4_write16(memPtr, value);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
+ }
+}
+
+/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
+LZ4_FORCE_O2_INLINE_GCC_PPC64LE
+void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+
+ do { memcpy(d,s,8); d+=8; s+=8; } while (d=2)
+# include
+static int g_debuglog_enable = 1;
+# define DEBUGLOG(l, ...) { \
+ if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
+ fprintf(stderr, __FILE__ ": "); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, " \n"); \
+ } }
+#else
+# define DEBUGLOG(l, ...) {} /* disabled */
+#endif
+
+
+/*-************************************
+* Common functions
+**************************************/
+static unsigned LZ4_NbCommonBytes (reg_t val)
+{
+ if (LZ4_isLittleEndian()) {
+ if (sizeof(val)==8) {
+ return (__builtin_ctzll((U64)val) >> 3);
+ } else /* 32 bits */ {
+ return (__builtin_ctz((U32)val) >> 3);
+ }
+ } else /* Big Endian CPU */ {
+ if (sizeof(val)==8) { /* 64-bits */
+ return (__builtin_clzll((U64)val) >> 3);
+ } else /* 32 bits */ {
+ return (__builtin_clz((U32)val) >> 3);
+ }
+ }
+}
+
+#define STEPSIZE sizeof(reg_t)
+LZ4_FORCE_INLINE
+unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+ const BYTE* const pStart = pIn;
+
+ if (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) {
+ pIn+=STEPSIZE; pMatch+=STEPSIZE;
+ } else {
+ return LZ4_NbCommonBytes(diff);
+ } }
+
+ while (likely(pIn < pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+ pIn += LZ4_NbCommonBytes(diff);
+ return (unsigned)(pIn - pStart);
+ }
+
+ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn compression run slower on incompressible data */
+
+
+/*-************************************
+* Local Structures and types
+**************************************/
+typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
+
+/**
+ * This enum distinguishes several different modes of accessing previous
+ * content in the stream.
+ *
+ * - noDict : There is no preceding content.
+ * - withPrefix64k : Table entries up to ctx->dictSize before the current blob
+ * blob being compressed are valid and refer to the preceding
+ * content (of length ctx->dictSize), which is available
+ * contiguously preceding in memory the content currently
+ * being compressed.
+ * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere
+ * else in memory, starting at ctx->dictionary with length
+ * ctx->dictSize.
+ * - usingDictCtx : Like usingExtDict, but everything concerning the preceding
+ * content is in a separate context, pointed to by
+ * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table
+ * entries in the current context that refer to positions
+ * preceding the beginning of the current compression are
+ * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
+ * ->dictSize describe the location and size of the preceding
+ * content, and matches are found by looking in the ctx
+ * ->dictCtx->hashTable.
+ */
+typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+
+/*-************************************
+* Local Utils
+**************************************/
+int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
+const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
+int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+
+
+/*-******************************
+* Compression functions
+********************************/
+static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+ if (tableType == byU16)
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+ else
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ static const U64 prime5bytes = 889523592379ULL;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+ if (LZ4_isLittleEndian())
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+ else
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
+{
+ switch (tableType)
+ {
+ default: /* fallthrough */
+ case clearedTable: /* fallthrough */
+ case byPtr: { /* illegal! */ return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)idx; return; }
+ }
+}
+
+static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case clearedTable: { /* illegal! */ return; }
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+ }
+}
+
+LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+/* LZ4_getIndexOnHash() :
+ * Index of match position registered in hash table.
+ * hash position must be calculated by using base+index, or dictBase+index.
+ * Assumption 1 : only valid if tableType == byU32 or byU16.
+ * Assumption 2 : h is presumed valid (within limits of hash table)
+ */
+static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
+{
+ LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h]; }
+ if (tableType == byU16) { const U16* const hashTable = (const U16*) tableBase; return hashTable[h]; }
+ return 0; /* forbidden case */
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+}
+
+LZ4_FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, const void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+LZ4_FORCE_INLINE void LZ4_prepareTable(
+ LZ4_stream_t_internal* const cctx,
+ const int inputSize,
+ const tableType_t tableType) {
+ /* If the table hasn't been used, it's guaranteed to be zeroed out, and is
+ * therefore safe to use no matter what mode we're in. Otherwise, we figure
+ * out if it's safe to leave as is or whether it needs to be reset.
+ */
+ if (cctx->tableType != clearedTable) {
+ if (cctx->tableType != tableType
+ || (tableType == byU16 && cctx->currentOffset + inputSize >= 0xFFFFU)
+ || (tableType == byU32 && cctx->currentOffset > 1 GB)
+ || tableType == byPtr
+ || inputSize >= 4 KB)
+ {
+ DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
+ MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
+ cctx->currentOffset = 0;
+ cctx->tableType = clearedTable;
+ } else {
+ DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
+ }
+ }
+
+ /* Adding a gap, so all previous entries are > MAX_DISTANCE back, is faster
+ * than compressing without a gap. However, compressing with
+ * currentOffset == 0 is faster still, so we preserve that case.
+ */
+ if (cctx->currentOffset != 0 && tableType == byU32) {
+ DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
+ cctx->currentOffset += 64 KB;
+ }
+
+ /* Finally, clear history */
+ cctx->dictCtx = NULL;
+ cctx->dictionary = NULL;
+ cctx->dictSize = 0;
+}
+
+/** LZ4_compress_generic() :
+ inlined, to ensure branches are decided at compilation time */
+LZ4_FORCE_INLINE int LZ4_compress_generic(
+ LZ4_stream_t_internal* const cctx,
+ const char* const source,
+ char* const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dictDirective,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE* ip = (const BYTE*) source;
+
+ U32 const startIndex = cctx->currentOffset;
+ const BYTE* base = (const BYTE*) source - startIndex;
+ const BYTE* lowLimit;
+
+ const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
+ const BYTE* const dictionary =
+ dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
+ const U32 dictSize =
+ dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
+ const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */
+
+ int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
+ U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
+ const BYTE* const dictEnd = dictionary + dictSize;
+ const BYTE* anchor = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ /* the dictCtx currentOffset is indexed on the start of the dictionary,
+ * while a dictionary in the current context precedes the currentOffset */
+ const BYTE* dictBase = dictDirective == usingDictCtx ?
+ dictionary + dictSize - dictCtx->currentOffset : /* is it possible that dictCtx->currentOffset != dictCtx->dictSize ? Yes if the dictionary context is not reset */
+ dictionary + dictSize - startIndex;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const olimit = op + maxOutputSize;
+
+ U32 offset = 0;
+ U32 forwardH;
+
+ DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType);
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
+
+ lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
+
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+
+ /* Update context state */
+ if (dictDirective == usingDictCtx) {
+ /* Subsequent linked blocks can't use the dictionary. */
+ /* Instead, they use the block we just compressed. */
+ cctx->dictCtx = NULL;
+ cctx->dictSize = (U32)inputSize;
+ } else {
+ cctx->dictSize += (U32)inputSize;
+ }
+ cctx->currentOffset += (U32)inputSize;
+ cctx->tableType = tableType;
+
+ if (inputSizehashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ if (tableType == byPtr) {
+ const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( (match+MAX_DISTANCE < ip)
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+
+ } else { /* byU32, byU16 */
+
+ const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ U32 const current = (U32)(forwardIp - base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
+
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ matchIndex += dictDelta; /* make dictCtx index comparable with current context */
+ lowLimit = dictionary;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source;
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source;
+ }
+ } else { /* single continuous memory segment */
+ match = base + matchIndex;
+ }
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+
+ if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) continue; /* match outside of valid area */
+ if ((tableType != byU16) && (current - matchIndex > MAX_DISTANCE)) continue; /* too far - note: works even if matchIndex overflows */
+
+ if (LZ4_read32(match) == LZ4_read32(ip)) {
+ if (maybe_extMem) offset = current - matchIndex;
+ break; /* match found */
+ }
+
+ } while(1);
+ }
+
+ /* Catch up */
+ while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+ /* Encode Literals */
+ { unsigned const litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if ((outputLimited) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength-RUN_MASK;
+ *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength< matchlimit) limit = matchlimit;
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip==limit) {
+ unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
+ matchCode += more;
+ ip += more;
+ }
+ DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);
+ } else {
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
+ DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);
+ }
+
+ if ( outputLimited && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) {
+ op+=4;
+ LZ4_write32(op, 0xFFFFFFFF);
+ matchCode -= 4*255;
+ }
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
+
+ /* Test end of chunk */
+ if (ip >= mflimitPlusOne) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+ /* Test next position */
+ if (tableType == byPtr) {
+
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( (match+MAX_DISTANCE >= ip)
+ && (LZ4_read32(match) == LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ } else { /* byU32, byU16 */
+
+ U32 const h = LZ4_hashPosition(ip, tableType);
+ U32 const current = (U32)(ip-base);
+ U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
+ if (dictDirective == usingDictCtx) {
+ if (matchIndex < startIndex) {
+ /* there was no match, try the dictionary */
+ matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ matchIndex += dictDelta;
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else if (dictDirective==usingExtDict) {
+ if (matchIndex < startIndex) {
+ match = dictBase + matchIndex;
+ lowLimit = dictionary; /* required for match length counter */
+ } else {
+ match = base + matchIndex;
+ lowLimit = (const BYTE*)source; /* required for match length counter */
+ }
+ } else { /* single memory segment */
+ match = base + matchIndex;
+ }
+ LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
+ if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
+ && ((tableType==byU16) ? 1 : (current - matchIndex <= MAX_DISTANCE))
+ && (LZ4_read32(match) == LZ4_read32(ip)) ) {
+ token=op++;
+ *token=0;
+ if (maybe_extMem) offset = current - matchIndex;
+ DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
+ goto _next_match;
+ }
+ }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ( (outputLimited) && /* Check output buffer overflow */
+ ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<internal_donotuse;
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit) {
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ if (inputSize < LZ4_64Klimit) {;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ }
+}
+
+/**
+ * LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
+ * "correctly initialized").
+ */
+int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ if (dstCapacity >= LZ4_compressBound(srcSize)) {
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ if (srcSize < LZ4_64Klimit) {
+ const tableType_t tableType = byU16;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ if (ctx->currentOffset) {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
+ } else {
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ } else {
+ const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ LZ4_prepareTable(ctx, srcSize, tableType);
+ return LZ4_compress_generic(ctx, src, dst, srcSize, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
+ }
+ }
+}
+
+
+int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ int result;
+ LZ4_stream_t ctx;
+ LZ4_stream_t* const ctxPtr = &ctx;
+ result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+ return result;
+}
+
+
+int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+ return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+}
+
+
+/* hidden debug function */
+/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
+int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t ctx;
+ LZ4_resetStream(&ctx);
+
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+}
+
+
+/*-******************************
+* *_destSize() variant
+********************************/
+
+static int LZ4_compress_destSize_generic(
+ LZ4_stream_t_internal* const ctx,
+ const char* const src,
+ char* const dst,
+ int* const srcSizePtr,
+ const int targetDstSize,
+ const tableType_t tableType)
+{
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* base = (const BYTE*) src;
+ const BYTE* lowLimit = (const BYTE*) src;
+ const BYTE* anchor = ip;
+ const BYTE* const iend = ip + *srcSizePtr;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + targetDstSize;
+ BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
+ BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
+ BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
+
+ U32 forwardH;
+
+
+ /* Init conditions */
+ if (targetDstSize < 1) return 0; /* Impossible to store anything */
+ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
+ if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (*srcSizePtrhashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = 1 << LZ4_skipTrigger;
+
+ do {
+ U32 h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base);
+
+ } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+ /* Encode Literal length */
+ { unsigned litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if (op + ((litLength+240)/255) + litLength > oMaxLit) {
+ /* Not enough space for a last match */
+ op--;
+ goto _last_literals;
+ }
+ if (litLength>=RUN_MASK) {
+ unsigned len = litLength - RUN_MASK;
+ *token=(RUN_MASK<= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength< oMaxMatch) {
+ /* Match description too long : reduce it */
+ matchLength = (15-1) + (oMaxMatch-op) * 255;
+ }
+ ip += MINMATCH + matchLength;
+
+ if (matchLength>=ML_MASK) {
+ *token += ML_MASK;
+ matchLength -= ML_MASK;
+ while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
+ *op++ = (BYTE)matchLength;
+ }
+ else *token += (BYTE)(matchLength);
+ }
+
+ anchor = ip;
+
+ /* Test end of block */
+ if (ip > mflimit) break;
+ if (op > oMaxSeq) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, ctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+ if ( (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t lastRunSize = (size_t)(iend - anchor);
+ if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) {
+ /* adapt lastRunSize to fill 'dst' */
+ lastRunSize = (oend-op) - 1;
+ lastRunSize -= (lastRunSize+240)/255;
+ }
+ ip = anchor + lastRunSize;
+
+ if (lastRunSize >= RUN_MASK) {
+ size_t accumulator = lastRunSize - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRunSize<= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
+ return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+ } else {
+ if (*srcSizePtr < LZ4_64Klimit) {
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16);
+ } else {
+ tableType_t const tableType = ((sizeof(void*)==4) && ((uptrval)src > MAX_DISTANCE)) ? byPtr : byU32;
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, tableType);
+ } }
+}
+
+
+int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+ LZ4_stream_t ctxBody;
+ LZ4_stream_t* ctx = &ctxBody;
+
+ int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+ return result;
+}
+
+
+
+/*-******************************
+* Streaming functions
+********************************/
+
+LZ4_stream_t* LZ4_createStream(void)
+{
+ LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
+ LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
+ DEBUGLOG(4, "LZ4_createStream %p", lz4s);
+ if (lz4s == NULL) return NULL;
+ LZ4_resetStream(lz4s);
+ return lz4s;
+}
+
+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+ DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
+ LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
+}
+
+int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
+{
+ if (!LZ4_stream) return 0; /* support free on NULL */
+ DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
+ FREEMEM(LZ4_stream);
+ return (0);
+}
+
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
+{
+ LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;
+ const tableType_t tableType = byU32;
+ const BYTE* p = (const BYTE*)dictionary;
+ const BYTE* const dictEnd = p + dictSize;
+ const BYTE* base;
+
+ DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
+
+ LZ4_prepareTable(dict, 0, tableType);
+
+ /* We always increment the offset by 64 KB, since, if the dict is longer,
+ * we truncate it to the last 64k, and if it's shorter, we still want to
+ * advance by a whole window length so we can provide the guarantee that
+ * there are only valid offsets in the window, which allows an optimization
+ * in LZ4_compress_fast_continue() where it uses noDictIssue even when the
+ * dictionary isn't a full 64k. */
+
+ if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
+ base = dictEnd - 64 KB - dict->currentOffset;
+ dict->dictionary = p;
+ dict->dictSize = (U32)(dictEnd - p);
+ dict->currentOffset += 64 KB;
+ dict->tableType = tableType;
+
+ if (dictSize < (int)HASH_UNIT) {
+ return 0;
+ }
+
+ while (p <= dictEnd-HASH_UNIT) {
+ LZ4_putPosition(p, dict->hashTable, tableType, base);
+ p+=3;
+ }
+
+ return dict->dictSize;
+}
+
+void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream) {
+ if (dictionary_stream != NULL) {
+ /* If the current offset is zero, we will never look in the
+ * external dictionary context, since there is no value a table
+ * entry can take that indicate a miss. In that case, we need
+ * to bump the offset to something non-zero.
+ */
+ if (working_stream->internal_donotuse.currentOffset == 0) {
+ working_stream->internal_donotuse.currentOffset = 64 KB;
+ }
+ working_stream->internal_donotuse.dictCtx = &(dictionary_stream->internal_donotuse);
+ } else {
+ working_stream->internal_donotuse.dictCtx = NULL;
+ }
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
+{
+ if (LZ4_dict->currentOffset + nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */
+ /* rescale hash table */
+ U32 const delta = LZ4_dict->currentOffset - 64 KB;
+ const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+ int i;
+ DEBUGLOG(4, "LZ4_renormDictT");
+ for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0;
+ else LZ4_dict->hashTable[i] -= delta;
+ }
+ LZ4_dict->currentOffset = 64 KB;
+ if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
+ LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+ }
+}
+
+
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ const tableType_t tableType = byU32;
+ LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;
+ const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+ if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
+ LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE* sourceEnd = (const BYTE*) source + inputSize;
+ if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+ streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+ if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
+ if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
+ streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+ }
+ }
+
+ /* prefix mode : source data follows dictionary */
+ if (dictEnd == (const BYTE*)source) {
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
+ else
+ return LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
+ }
+
+ /* external dictionary mode */
+ { int result;
+ if (streamPtr->dictCtx) {
+ /* We depend here on the fact that dictCtx'es (produced by
+ * LZ4_loadDict) guarantee that their tables contain no references
+ * to offsets between dictCtx->currentOffset - 64 KB and
+ * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
+ * to use noDictIssue even when the dict isn't a full 64 KB.
+ */
+ if (inputSize > 4 KB) {
+ /* For compressing large blobs, it is faster to pay the setup
+ * cost to copy the dictionary's tables into the active context,
+ * so that the compression loop is only looking into one table.
+ */
+ memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t));
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
+ }
+ } else {
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
+ }
+ }
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)inputSize;
+ return result;
+ }
+}
+
+
+/* Hidden debug function, to force-test external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
+{
+ LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;
+ int result;
+
+ LZ4_renormDictT(streamPtr, srcSize);
+
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
+ } else {
+ result = LZ4_compress_generic(streamPtr, source, dest, srcSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+ }
+
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)srcSize;
+
+ return result;
+}
+
+
+/*! LZ4_saveDict() :
+ * If previously compressed data block is not guaranteed to remain available at its memory location,
+ * save it into a safer place (char* safeBuffer).
+ * Note : you don't need to call LZ4_loadDict() afterwards,
+ * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
+{
+ LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+ const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
+
+ if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
+ if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
+
+ memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+ dict->dictionary = (const BYTE*)safeBuffer;
+ dict->dictSize = (U32)dictSize;
+
+ return dictSize;
+}
+
+
+
+/*-*****************************
+* Decompression functions
+*******************************/
+/*! LZ4_decompress_generic() :
+ * This generic decompression function covers all use cases.
+ * It shall be instantiated several times, using different sets of directives.
+ * Note that it is important for performance that this function really get inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+LZ4_FORCE_O2_GCC_PPC64LE
+LZ4_FORCE_INLINE int LZ4_decompress_generic(
+ const char* const src,
+ char* const dst,
+ int srcSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* const iend = ip + srcSize;
+
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4};
+ const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => just decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((srcSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+ /* Main Loop : decode sequences */
+ while (1) {
+ size_t length;
+ const BYTE* match;
+ size_t offset;
+
+ unsigned const token = *ip++;
+
+ /* shortcut for common case :
+ * in most circumstances, we expect to decode small matches (<= 18 bytes) separated by few literals (<= 14 bytes).
+ * this shortcut was tested on x86 and x64, where it improves decoding speed.
+ * it has not yet been benchmarked on ARM, Power, mips, etc. */
+ if (((ip + 14 /*maxLL*/ + 2 /*offset*/ <= iend)
+ & (op + 14 /*maxLL*/ + 18 /*maxML*/ <= oend))
+ & ((token < (15<> ML_BITS;
+ size_t const off = LZ4_readLE16(ip+ll);
+ const BYTE* const matchPtr = op + ll - off; /* pointer underflow risk ? */
+ if ((off >= 8) /* do not deal with overlapping matches */ & (matchPtr >= lowPrefix)) {
+ size_t const ml = (token & ML_MASK) + MINMATCH;
+ memcpy(op, ip, 16); op += ll; ip += ll + 2 /*offset*/;
+ memcpy(op + 0, matchPtr + 0, 8);
+ memcpy(op + 8, matchPtr + 8, 8);
+ memcpy(op +16, matchPtr +16, 2);
+ op += ml;
+ continue;
+ }
+ }
+
+ /* decode literal length */
+ if ((length=(token>>ML_BITS)) == RUN_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ length += s;
+ } while ( likely(endOnInput ? ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ if (partialDecoding) {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ } else {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ memcpy(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */
+ LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match can be copied as a single segment from external dictionary */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match encompass external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix-match);
+ size_t const restSize = length - copySize;
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset<8)) {
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += inc32table[offset];
+ memcpy(op+4, match, 4);
+ match -= dec64table[offset];
+ } else { memcpy(op, match, 8); match+=8; }
+ op += 8;
+
+ if (unlikely(cpy>oend-12)) {
+ BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op16) LZ4_wildCopy(op+8, match+8, cpy);
+ }
+ op = cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dst); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-src); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-src))-1;
+}
+
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
+}
+
+
+/*===== streaming decompression functions =====*/
+
+LZ4_streamDecode_t* LZ4_createStreamDecode(void)
+{
+ LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
+ return lz4s;
+}
+
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
+{
+ if (!LZ4_stream) return 0; /* support free on NULL */
+ FREEMEM(LZ4_stream);
+ return 0;
+}
+
+/*!
+ * LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ lz4sd->prefixSize = (size_t) dictSize;
+ lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
+ lz4sd->externalDict = NULL;
+ lz4sd->extDictSize = 0;
+ return 1;
+}
+
+/*
+*_continue() :
+ These decoding functions allow decompression of multiple blocks in "streaming" mode.
+ Previously decoded blocks must still be available at the memory position where they were decoded.
+ If it's not possible, save the relevant part of decoded data into a safe buffer,
+ and indicate where it stands using LZ4_setStreamDecode()
+*/
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += result;
+ lz4sd->prefixEnd += result;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = result;
+ lz4sd->prefixEnd = (BYTE*)dest + result;
+ }
+
+ return result;
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += originalSize;
+ lz4sd->prefixEnd += originalSize;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = originalSize;
+ lz4sd->prefixEnd = (BYTE*)dest + originalSize;
+ }
+
+ return result;
+}
+
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+ These decoding functions work the same as "_continue" ones,
+ the dictionary must be explicitly provided within parameters
+*/
+
+LZ4_FORCE_O2_GCC_PPC64LE
+LZ4_FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
+{
+ if (dictSize==0)
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
+ if (dictStart+dictSize == dest) {
+ if (dictSize >= (int)(64 KB - 1))
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
+ }
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+}
+
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
+}
+
+/* debug function */
+LZ4_FORCE_O2_GCC_PPC64LE
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+
+/*=*************************************************
+* Obsolete Functions
+***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
+int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
+
+/*
+These function names are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
+
+int LZ4_resetStreamState(void* state, char* inputBuffer)
+{
+ (void)inputBuffer;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ return 0;
+}
+
+void* LZ4_create (char* inputBuffer)
+{
+ (void)inputBuffer;
+ return LZ4_createStream();
+}
+
+char* LZ4_slideInputBuffer (void* state)
+{
+ /* avoid const char * -> char * conversion warning */
+ return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
+}
+
+/* Obsolete streaming decompression functions */
+
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+#endif /* LZ4_COMMONDEFS_ONLY */
diff --git a/bdk/libs/compr/lz4.h b/bdk/libs/compr/lz4.h
new file mode 100644
index 00000000..0dfa19e0
--- /dev/null
+++ b/bdk/libs/compr/lz4.h
@@ -0,0 +1,569 @@
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Header File
+ * Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
+/* --- Dependency --- */
+#include /* size_t */
+
+
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+ multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+ The LZ4 compression library provides in-memory compression and decompression functions.
+ Compression can be done in:
+ - a single step (described as Simple Functions)
+ - a single step, reusing a context (described in Advanced Functions)
+ - unbounded multiple steps (described as Streaming compression)
+
+ lz4.h provides block compression functions. It gives full buffer control to user.
+ Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+ Each application is free to encode such metadata in whichever way it wants.
+
+ An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+ take care of encoding standard metadata alongside LZ4-compressed blocks.
+ If your application requires interoperability, it's recommended to use it.
+ A library is provided to take care of it, see lz4frame.h.
+*/
+
+/*^***************************************************************
+* Export parameters
+*****************************************************************/
+/*
+* LZ4_DLL_EXPORT :
+* Enable exporting of functions when building a Windows DLL
+* LZ4LIB_VISIBILITY :
+* Control library symbols visibility.
+*/
+#ifndef LZ4LIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define LZ4LIB_VISIBILITY
+# endif
+#endif
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define LZ4LIB_API LZ4LIB_VISIBILITY
+#endif
+
+/*------ Version ------*/
+#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
+#define LZ4_VERSION_MINOR 8 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */
+
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+
+LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
+LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; unseful to check dll version */
+
+
+/*-************************************
+* Tuning parameter
+**************************************/
+/*!
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage may improve speed, thanks to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#ifndef LZ4_MEMORY_USAGE
+# define LZ4_MEMORY_USAGE 14
+#endif
+
+/*-************************************
+* Simple Functions
+**************************************/
+/*! LZ4_compress_default() :
+ Compresses 'srcSize' bytes from buffer 'src'
+ into already allocated 'dst' buffer of size 'dstCapacity'.
+ Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
+ It also runs faster, so it's a recommended setting.
+ If the function cannot compress 'src' into a more limited 'dst' budget,
+ compression stops *immediately*, and the function result is zero.
+ Note : as a consequence, 'dst' content is not valid.
+ Note 2 : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
+ srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
+ dstCapacity : size of buffer 'dst' (which must be already allocated)
+ return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
+ or 0 if compression fails */
+LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
+
+/*! LZ4_decompress_safe() :
+ compressedSize : is the exact complete size of the compressed block.
+ dstCapacity : is the size of destination buffer, which must be already allocated.
+ return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
+ If destination buffer is not large enough, decoding will stop and output an error code (negative value).
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function is protected against malicious data packets.
+*/
+LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
+
+
+/*-************************************
+* Advanced Functions
+**************************************/
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+/*!
+LZ4_compressBound() :
+ Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+ This function is primarily useful for memory allocation purposes (destination buffer size).
+ Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+ Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
+ inputSize : max supported value is LZ4_MAX_INPUT_SIZE
+ return : maximum output size in a "worst case" scenario
+ or 0, if input size is incorrect (too large or negative)
+*/
+LZ4LIB_API int LZ4_compressBound(int inputSize);
+
+/*!
+LZ4_compress_fast() :
+ Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
+ The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+ It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+ An acceleration value of "1" is the same as regular LZ4_compress_default()
+ Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c).
+*/
+LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*!
+LZ4_compress_fast_extState() :
+ Same compression function, just using an externally allocated memory space to store compression state.
+ Use LZ4_sizeofState() to know how much memory must be allocated,
+ and allocate it on 8-bytes boundaries (using malloc() typically).
+ Then, provide it as 'void* state' to compression function.
+*/
+LZ4LIB_API int LZ4_sizeofState(void);
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+
+/*!
+LZ4_compress_destSize() :
+ Reverse the logic : compresses as much data as possible from 'src' buffer
+ into already allocated buffer 'dst' of size 'targetDestSize'.
+ This function either compresses the entire 'src' content into 'dst' if it's large enough,
+ or fill 'dst' buffer completely with as much data as possible from 'src'.
+ *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
+ New value is necessarily <= old value.
+ return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
+ or 0 if compression fails
+*/
+LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
+
+
+/*!
+LZ4_decompress_fast() : **unsafe!**
+This function is a bit faster than LZ4_decompress_safe(),
+but doesn't provide any security guarantee.
+ originalSize : is the uncompressed size to regenerate
+ Destination buffer must be already allocated, and its size must be >= 'originalSize' bytes.
+ return : number of bytes read from source buffer (== compressed size).
+ If the source stream is detected malformed, the function stops decoding and return a negative result.
+ note : This function respects memory boundaries for *properly formed* compressed data.
+ However, it does not provide any protection against malicious input.
+ It also doesn't know 'src' size, and implies it's >= compressed size.
+ Use this function in trusted environment **only**.
+*/
+LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
+
+/*!
+LZ4_decompress_safe_partial() :
+ This function decompress a compressed block of size 'srcSize' at position 'src'
+ into destination buffer 'dst' of size 'dstCapacity'.
+ The function will decompress a minimum of 'targetOutputSize' bytes, and stop after that.
+ However, it's not accurate, and may write more than 'targetOutputSize' (but always <= dstCapacity).
+ @return : the number of bytes decoded in the destination buffer (necessarily <= dstCapacity)
+ Note : this number can also be < targetOutputSize, if compressed block contains less data.
+ Therefore, always control how many bytes were decoded.
+ If source stream is detected malformed, function returns a negative result.
+ This function is protected against malicious data packets.
+*/
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
+
+
+/*-*********************************************
+* Streaming Compression Functions
+***********************************************/
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStream() and LZ4_freeStream() :
+ * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+ * LZ4_freeStream() releases its memory.
+ */
+LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
+LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_resetStream() :
+ * An LZ4_stream_t structure can be allocated once and re-used multiple times.
+ * Use this function to start compressing a new stream.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_loadDict() :
+ * Use this function to load a static dictionary into LZ4_stream_t.
+ * Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ * Loading a size of 0 is allowed, and is the same as reset.
+ * @return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+
+/*! LZ4_compress_fast_continue() :
+ * Compress 'src' content using data from previously compressed blocks, for better compression ratio.
+ * 'dst' buffer must be already allocated.
+ * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ *
+ * Important : The previous 64KB of compressed data is assumed to remain present and unmodified in memory!
+ *
+ * Special 1 : When input is a double-buffer, they can have any size, including < 64 KB.
+ * Make sure that buffers are separated by at least one byte.
+ * This way, each block only depends on previous block.
+ * Special 2 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
+ *
+ * @return : size of compressed block
+ * or 0 if there is an error (typically, cannot fit into 'dst').
+ * After an error, the stream status is invalid, it can only be reset or freed.
+ */
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_saveDict() :
+ * If last 64KB data cannot be guaranteed to remain available at its current memory location,
+ * save it into a safer place (char* safeBuffer).
+ * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
+ * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
+ * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
+ */
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
+
+
+/*-**********************************************
+* Streaming Decompression Functions
+* Bufferless synchronous API
+************************************************/
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
+ * creation / destruction of streaming decompression tracking structure.
+ * A tracking structure can be re-used multiple times sequentially. */
+LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+
+/*! LZ4_setStreamDecode() :
+ * An LZ4_streamDecode_t structure can be allocated once and re-used multiple times.
+ * Use this function to start decompression of a new stream of blocks.
+ * A dictionary can optionnally be set. Use NULL or size 0 for a reset order.
+ * @return : 1 if OK, 0 if error
+ */
+LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+
+/*! LZ4_decompress_*_continue() :
+ * These decoding functions allow decompression of consecutive blocks in "streaming" mode.
+ * A block is an unsplittable entity, it must be presented entirely to a decompression function.
+ * Decompression functions only accept one block at a time.
+ * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.
+ * If less than 64KB of data has been decoded all the data must be present.
+ *
+ * Special : if application sets a ring buffer for decompression, it must respect one of the following conditions :
+ * - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+ * In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+ * - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ * maxBlockSize is implementation dependent. It's the maximum size of any single block.
+ * In which case, encoding and decoding buffers do not need to be synchronized,
+ * and encoding ring buffer can have any size, including small ones ( < 64 KB).
+ * - _At least_ 64 KB + 8 bytes + maxBlockSize.
+ * In which case, encoding and decoding buffers do not need to be synchronized,
+ * and encoding ring buffer can have any size, including larger than decoding buffer.
+ * Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+ * and indicate where it is saved using LZ4_setStreamDecode() before decompressing next block.
+*/
+LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
+
+
+/*! LZ4_decompress_*_usingDict() :
+ * These decoding functions work the same as
+ * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
+ * They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ */
+LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
+
+
+/*^**********************************************
+ * !!!!!! STATIC LINKING ONLY !!!!!!
+ ***********************************************/
+
+/*-************************************
+ * Unstable declarations
+ **************************************
+ * Declarations in this section should be considered unstable.
+ * Use at your own peril, etc., etc.
+ * They may be removed in the future.
+ * Their signatures may change.
+ **************************************/
+
+#ifdef LZ4_STATIC_LINKING_ONLY
+
+/*! LZ4_resetStream_fast() :
+ * When an LZ4_stream_t is known to be in a internally coherent state,
+ * it can often be prepared for a new compression with almost no work, only
+ * sometimes falling back to the full, expensive reset that is always required
+ * when the stream is in an indeterminate state (i.e., the reset performed by
+ * LZ4_resetStream()).
+ *
+ * LZ4_streams are guaranteed to be in a valid state when:
+ * - returned from LZ4_createStream()
+ * - reset by LZ4_resetStream()
+ * - memset(stream, 0, sizeof(LZ4_stream_t))
+ * - the stream was in a valid state and was reset by LZ4_resetStream_fast()
+ * - the stream was in a valid state and was then used in any compression call
+ * that returned success
+ * - the stream was in an indeterminate state and was used in a compression
+ * call that fully reset the state (LZ4_compress_fast_extState()) and that
+ * returned success
+ */
+LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
+
+/*! LZ4_compress_fast_extState_fastReset() :
+ * A variant of LZ4_compress_fast_extState().
+ *
+ * Using this variant avoids an expensive initialization step. It is only safe
+ * to call if the state buffer is known to be correctly initialized already
+ * (see above comment on LZ4_resetStream_fast() for a definition of "correctly
+ * initialized"). From a high level, the difference is that this function
+ * initializes the provided state with a call to LZ4_resetStream_fast() while
+ * LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
+ */
+LZ4LIB_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_attach_dictionary() :
+ * This is an experimental API that allows for the efficient use of a
+ * static dictionary many times.
+ *
+ * Rather than re-loading the dictionary buffer into a working context before
+ * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
+ * working LZ4_stream_t, this function introduces a no-copy setup mechanism,
+ * in which the working stream references the dictionary stream in-place.
+ *
+ * Several assumptions are made about the state of the dictionary stream.
+ * Currently, only streams which have been prepared by LZ4_loadDict() should
+ * be expected to work.
+ *
+ * Alternatively, the provided dictionary stream pointer may be NULL, in which
+ * case any existing dictionary stream is unset.
+ *
+ * If a dictionary is provided, it replaces any pre-existing stream history.
+ * The dictionary contents are the only history that can be referenced and
+ * logically immediately precede the data compressed in the first subsequent
+ * compression call.
+ *
+ * The dictionary will only remain attached to the working stream through the
+ * first compression call, at the end of which it is cleared. The dictionary
+ * stream (and source buffer) must remain in-place / accessible / unchanged
+ * through the completion of the first compression call on the stream.
+ */
+LZ4LIB_API void LZ4_attach_dictionary(LZ4_stream_t *working_stream, const LZ4_stream_t *dictionary_stream);
+
+#endif
+
+/*-************************************
+ * Private definitions
+ **************************************
+ * Do not use these definitions.
+ * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Using these definitions will expose code to API and/or ABI break in future versions of the library.
+ **************************************/
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#include
+
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint16_t initCheck;
+ uint16_t tableType;
+ const uint8_t* dictionary;
+ const LZ4_stream_t_internal* dictCtx;
+ uint32_t dictSize;
+};
+
+typedef struct {
+ const uint8_t* externalDict;
+ size_t extDictSize;
+ const uint8_t* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#else
+
+typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
+struct LZ4_stream_t_internal {
+ unsigned int hashTable[LZ4_HASH_SIZE_U32];
+ unsigned int currentOffset;
+ unsigned short initCheck;
+ unsigned short tableType;
+ const unsigned char* dictionary;
+ const LZ4_stream_t_internal* dictCtx;
+ unsigned int dictSize;
+};
+
+typedef struct {
+ const unsigned char* externalDict;
+ size_t extDictSize;
+ const unsigned char* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#endif
+
+/*!
+ * LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ * init this structure before first use.
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * it may change in a future version !
+ */
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_stream_t */
+
+
+/*!
+ * LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure using LZ4_setStreamDecode (or memset()) before first use
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * and may change in a future version !
+ */
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_streamDecode_t */
+
+
+/*-************************************
+* Obsolete Functions
+**************************************/
+
+/*! Deprecation warnings
+ Should deprecation warnings be a problem,
+ it is generally possible to disable them,
+ typically with -Wno-deprecated-declarations for gcc
+ or _CRT_SECURE_NO_WARNINGS in Visual.
+ Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
+#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
+# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
+#else
+# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define LZ4_DEPRECATED(message) [[deprecated(message)]]
+# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+# elif (LZ4_GCC_VERSION >= 301)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
+# define LZ4_DEPRECATED(message)
+# endif
+#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/* Obsolete compression functions */
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* source, char* dest, int sourceSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Obsolete decompression functions */
+LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
+LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
+
+/* Obsolete streaming functions; degraded functionality; do not use!
+ *
+ * In order to perform streaming compression, these functions depended on data
+ * that is no longer tracked in the state. They have been preserved as well as
+ * possible: using them will still produce a correct output. However, they don't
+ * actually retain any history between compression calls. The compression ratio
+ * achieved will therefore be no better than compressing each chunk
+ * independently.
+ */
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
+
+/* Obsolete streaming decoding functions */
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+#endif /* LZ4_H_2983827168210 */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/bdk/libs/fatfs/diskio.h b/bdk/libs/fatfs/diskio.h
new file mode 100644
index 00000000..6959fb4f
--- /dev/null
+++ b/bdk/libs/fatfs/diskio.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------------/
+/ Low level disk interface modlue include file (C)ChaN, 2014 /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/* 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;
+
+typedef enum {
+ DRIVE_SD = 0,
+ DRIVE_RAM = 1,
+ DRIVE_EMMC = 2,
+ DRIVE_BIS = 3
+} DDRIVE;
+
+
+/*---------------------------------------*/
+/* 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);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+DRESULT disk_set_info (BYTE pdrv, BYTE cmd, void *buff);
+
+
+/* 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 FF_FS_READONLY == 0) */
+#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
+#define SET_SECTOR_COUNT 1 /* Set media size (needed at FF_USE_MKFS == 1) */
+#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
+#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
+#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
+
+/* Generic command (Not used by FatFs) */
+#define CTRL_POWER 5 /* Get/Set power status */
+#define CTRL_LOCK 6 /* Lock/Unlock media removal */
+#define CTRL_EJECT 7 /* Eject media */
+#define CTRL_FORMAT 8 /* Create physical format on the media */
+
+/* MMC/SDC specific ioctl command */
+#define MMC_GET_TYPE 10 /* Get card type */
+#define MMC_GET_CSD 11 /* Get CSD */
+#define MMC_GET_CID 12 /* Get CID */
+#define MMC_GET_OCR 13 /* Get OCR */
+#define MMC_GET_SDSTAT 14 /* 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 */
+#define ATA_GET_REV 20 /* Get F/W revision */
+#define ATA_GET_MODEL 21 /* Get model name */
+#define ATA_GET_SN 22 /* Get serial number */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bdk/libs/fatfs/ff.c b/bdk/libs/fatfs/ff.c
new file mode 100644
index 00000000..60288911
--- /dev/null
+++ b/bdk/libs/fatfs/ff.c
@@ -0,0 +1,6861 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018-2019 CTCaer
+ * Copyright (c) 2020 Storm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem Module R0.13c (p4) /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2018, 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 */
+#include
+
+#define EFSPRINTF(text, ...) print_error(); gfx_printf("%k"text"%k\n", 0xFFFFFF00, 0xFFFFFFFF);
+//#define EFSPRINTF(...)
+
+/*--------------------------------------------------------------------------
+
+ Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if FF_DEFINED != 86604 /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* 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 right for real DOS/Windows behavior) */
+#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right 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) */
+
+
+/* Character code support macros */
+#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z')
+#define IsLower(c) ((c) >= 'a' && (c) <= 'z')
+#define IsDigit(c) ((c) >= '0' && (c) <= '9')
+#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
+#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
+#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
+
+
+/* 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 */
+
+
+/* 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 */
+
+
+/* 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 */
+
+
+/* exFAT directory entry types */
+#define ET_BITMAP 0x81 /* Allocation bitmap */
+#define ET_UPCASE 0x82 /* Up-case table */
+#define ET_VLABEL 0x83 /* Volume label */
+#define ET_FILEDIR 0x85 /* File and directory */
+#define ET_STREAM 0xC0 /* Stream extension */
+#define ET_FILENAME 0xC1 /* Name extension */
+
+
+/* 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 (WORD) */
+#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 on fatal error in the file operations */
+#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Re-entrancy 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 - physical location 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; /* Filesystem mount ID */
+
+#if FF_FS_RPATH != 0
+static BYTE CurrVol; /* Current drive */
+#endif
+
+#if FF_FS_LOCK != 0
+static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifdef FF_VOLUME_STRS
+static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */
+#endif
+#endif
+
+
+/*--------------------------------*/
+/* LFN/Directory working buffer */
+/*--------------------------------*/
+
+#if FF_USE_LFN == 0 /* Non-LFN configuration */
+#if FF_FS_EXFAT
+#error LFN must be enabled when enable exFAT
+#endif
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+#define LEAVE_MKFS(res) return res
+
+#else /* LFN configurations */
+#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
+#error Wrong setting of FF_MAX_LFN
+#endif
+#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
+#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
+#endif
+#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
+#error Wrong setting of FF_LFN_UNICODE
+#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 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()
+#define LEAVE_MKFS(res) return res
+
+#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
+#define LEAVE_MKFS(res) return res
+
+#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
+#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
+#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */
+
+#else
+#error Wrong setting of FF_USE_LFN
+
+#endif /* FF_USE_LFN == 1 */
+#endif /* FF_USE_LFN == 0 */
+
+
+
+/*--------------------------------*/
+/* 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
+
+---------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------*/
+/* Print error header */
+/*-----------------------------------------------------------------------*/
+
+void print_error()
+{
+ gfx_printf("\n\n\n%k[FatFS] Error: %k", 0xFFFFFF00, 0xFFFFFFFF);
+}
+
+
+/*-----------------------------------------------------------------------*/
+/* 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 != 0) 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 /* DBCS 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 != 0) return 0; /* Always false */
+#endif
+ return 0;
+}
+
+
+#if FF_USE_LFN
+
+/* Get a character from TCHAR string in defined API encodeing */
+static DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */
+ const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */
+)
+{
+ DWORD uc;
+ const TCHAR *p = *str;
+
+#if FF_LFN_UNICODE == 1 /* UTF-16 input */
+ WCHAR wc;
+
+ uc = *p++; /* Get a unit */
+ if (IsSurrogate(uc)) { /* Surrogate? */
+ wc = *p++; /* Get low surrogate */
+ if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */
+ uc = uc << 16 | wc;
+ }
+
+#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
+ BYTE b;
+ int nf;
+
+ uc = (BYTE)*p++; /* Get a unit */
+ if (uc & 0x80) { /* Multiple byte code? */
+ if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */
+ uc &= 0x1F; nf = 1;
+ } else {
+ if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */
+ uc &= 0x0F; nf = 2;
+ } else {
+ if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */
+ uc &= 0x07; nf = 3;
+ } else { /* Wrong sequence */
+ return 0xFFFFFFFF;
+ }
+ }
+ }
+ do { /* Get trailing bytes */
+ b = (BYTE)*p++;
+ if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */
+ uc = uc << 6 | (b & 0x3F);
+ } while (--nf != 0);
+ if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
+ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+ }
+
+#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
+ uc = (TCHAR)*p++; /* Get a unit */
+ if (uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */
+ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+
+#else /* ANSI/OEM input */
+ BYTE b;
+ WCHAR wc;
+
+ wc = (BYTE)*p++; /* Get a byte */
+ if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */
+ b = (BYTE)*p++; /* Get 2nd byte */
+ if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */
+ wc = (wc << 8) + b; /* Make a DBC */
+ }
+ if (wc != 0) {
+ wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */
+ if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */
+ }
+ uc = wc;
+
+#endif
+ *str = p; /* Next read pointer */
+ return uc;
+}
+
+
+/* Output a TCHAR string in defined API encoding */
+static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
+ DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */
+ TCHAR* buf, /* Output buffer */
+ UINT szb /* Size of the buffer */
+)
+{
+#if FF_LFN_UNICODE == 1 /* UTF-16 output */
+ WCHAR hs, wc;
+
+ hs = (WCHAR)(chr >> 16);
+ wc = (WCHAR)chr;
+ if (hs == 0) { /* Single encoding unit? */
+ if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */
+ *buf = wc;
+ return 1;
+ }
+ if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */
+ *buf++ = hs;
+ *buf++ = wc;
+ return 2;
+
+#elif FF_LFN_UNICODE == 2 /* UTF-8 output */
+ DWORD hc;
+
+ if (chr < 0x80) { /* Single byte code? */
+ if (szb < 1) return 0; /* Buffer overflow? */
+ *buf = (TCHAR)chr;
+ return 1;
+ }
+ if (chr < 0x800) { /* 2-byte sequence? */
+ if (szb < 2) return 0; /* Buffer overflow? */
+ *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 2;
+ }
+ if (chr < 0x10000) { /* 3-byte sequence? */
+ if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */
+ *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 3;
+ }
+ /* 4-byte sequence */
+ if (szb < 4) return 0; /* Buffer overflow? */
+ hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
+ chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
+ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
+ chr = (hc | chr) + 0x10000;
+ *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07));
+ *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+ *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+ return 4;
+
+#elif FF_LFN_UNICODE == 3 /* UTF-32 output */
+ DWORD hc;
+
+ if (szb < 1) return 0; /* Buffer overflow? */
+ if (chr >= 0x10000) { /* Out of BMP? */
+ hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */
+ chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */
+ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */
+ chr = (hc | chr) + 0x10000;
+ }
+ *buf++ = (TCHAR)chr;
+ return 1;
+
+#else /* ANSI/OEM output */
+ WCHAR wc;
+
+ wc = ff_uni2oem(chr, CODEPAGE);
+ if (wc >= 0x100) { /* Is this a DBC? */
+ if (szb < 2) return 0;
+ *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */
+ *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */
+ return 2;
+ }
+ if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */
+ *buf++ = (TCHAR)wc; /* Store the character */
+ return 1;
+#endif
+}
+#endif /* FF_USE_LFN */
+
+
+#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 >= 1 && 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, sizeof fs->win);
+ 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 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */
+ 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->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
+ 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 == 0) { /* 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 != 0);
+ 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->bitbase + clst / 8 / SS(fs); /* Sector address */
+ 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 chain of last fragment */
+ 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 if 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, sizeof fs->win); /* Clear window buffer */
+#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
+ /* Allocate a temporary buffer */
+ for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; 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 (many 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 (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */
+ if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */
+
+ 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 != 0) {
+ if (i >= FF_MAX_LFN + 1 || 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 != 0) {
+ if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */
+ lfnbuf[i++] = wc = uc; /* Store it */
+ } else {
+ if (uc != 0xFFFF) return 0; /* Check filler */
+ }
+ }
+
+ if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it is the last LFN part and not terminated */
+ if (i >= FF_MAX_LFN + 1) 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 as hash value */
+ 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; /* Number of bytes of the entry block */
+ for (i = sum = 0; i < szblk; i++) {
+ if (i == XDIR_SetSum) { /* Skip 2-byte 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 file name */
+ const WCHAR* name /* File name to be calculated */
+)
+{
+ WCHAR chr;
+ WORD sum = 0;
+
+
+ while ((chr = *name++) != 0) {
+ chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-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 ( /* Returns 32-bit checksum */
+ BYTE dat, /* Byte to be calculated (byte-by-byte processing) */
+ DWORD sum /* Previous sum value */
+)
+{
+ 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_xfileinfo (
+ BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */
+ FILINFO* fno /* Buffer to store the extracted file information */
+)
+{
+ WCHAR wc, hs;
+ UINT di, si, nc;
+
+ /* Get file name from the entry block */
+ si = SZDIRE * 2; /* 1st C1 entry */
+ nc = 0; hs = 0; di = 0;
+ while (nc < dirb[XDIR_NumName]) {
+ if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */
+ if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */
+ wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */
+ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
+ hs = wc; continue; /* Get low surrogate */
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */
+ if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */
+ fno->fname[di] = 0; /* Terminate the name */
+ fno->altname[0] = 0; /* exFAT does not support 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 file-directory entry */
+ res = move_window(dp->obj.fs, dp->sect);
+ if (res != FR_OK) return res;
+ if (dp->dir[XDIR_Type] != ET_FILEDIR) 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 stream-extension 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] != ET_STREAM) 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 file-name entries */
+ i = 2 * SZDIRE; /* Name 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] != ET_FILENAME) 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;
+}
+
+
+/*------------------------------------------------------------------*/
+/* exFAT: Initialize object allocation info with loaded entry block */
+/*------------------------------------------------------------------*/
+
+static void init_alloc_info (
+ FATFS* fs, /* Filesystem object */
+ FFOBJID* obj /* Object allocation information to be initialized */
+)
+{
+ obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */
+ obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */
+ obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */
+ obj->n_frag = 0; /* No last fragment info */
+}
+
+
+
+#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 wc;
+
+
+ /* Create file-directory and stream-extension entry */
+ mem_set(dirb, 0, 2 * SZDIRE);
+ dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR;
+ dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM;
+
+ /* Create file-name entries */
+ i = SZDIRE * 2; /* Top of file_name entries */
+ nlen = nc1 = 0; wc = 1;
+ do {
+ dirb[i++] = ET_FILENAME; dirb[i++] = 0;
+ do { /* Fill name field */
+ if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */
+ st_word(dirb + i, wc); /* 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 (C0 + C1s) */
+ 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 */
+/*-----------------------------------------------------------------------*/
+
+#define DIR_READ_FILE(dp) dir_read(dp, 0)
+#define DIR_READ_LABEL(dp) dir_read(dp, 1)
+
+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 attr, b;
+#if FF_USE_LFN
+ BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+ while (dp->sect) {
+ res = move_window(fs, dp->sect);
+ if (res != FR_OK) break;
+ b = dp->dir[DIR_Name]; /* Test for the entry type */
+ if (b == 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 (b == ET_VLABEL) break; /* Volume label entry? */
+ } else {
+ if (b == ET_FILEDIR) { /* 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 = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */
+#if FF_USE_LFN /* LFN configuration */
+ if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (attr == AM_LFN) { /* An LFN entry is found */
+ if (b & LLEF) { /* Is it start of an LFN sequence? */
+ sum = dp->dir[LDIR_Chksum];
+ b &= (BYTE)~LLEF; ord = b;
+ dp->blk_ofs = dp->dptr;
+ }
+ /* Check LFN validity and capture it */
+ ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+ } else { /* An SFN entry is found */
+ if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */
+ dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */
+ }
+ break;
+ }
+ }
+#else /* Non LFN configuration */
+ if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~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_FILE(dp)) == 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 == 0 && 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 directory 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 by new allocation? */
+ 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;
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */
+ } else { /* On the FAT/FAT32 volume */
+ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */
+ }
+ 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; /* Mark the entry 'deleted'.*/
+ 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 (
+ DIR* dp, /* Pointer to the directory object */
+ FILINFO* fno /* Pointer to the file information to be filled */
+)
+{
+ UINT si, di;
+#if FF_USE_LFN
+ BYTE lcf;
+ WCHAR wc, hs;
+ FATFS *fs = dp->obj.fs;
+#else
+ TCHAR c;
+#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_xfileinfo(fs->dirbuf, fno);
+ return;
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
+ si = di = hs = 0;
+ while (fs->lfnbuf[si] != 0) {
+ wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */
+ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */
+ hs = wc; continue; /* Get low surrogate */
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */
+ if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */
+ }
+ }
+
+ si = di = 0;
+ while (si < 11) { /* Get SFN from SFN entry */
+ wc = dp->dir[si++]; /* Get a char */
+ if (wc == ' ') continue; /* Skip padding spaces */
+ if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */
+ if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */
+#if FF_LFN_UNICODE >= 1 /* Unicode output */
+ if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */
+ wc = wc << 8 | dp->dir[si++];
+ }
+ wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */
+ if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */
+ wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */
+ if (wc == 0) { di = 0; break; } /* Buffer overflow? */
+ di += wc;
+#else /* ANSI/OEM output */
+ fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
+#endif
+ }
+ fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */
+
+ if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
+ if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */
+ fno->fname[di++] = '?';
+ } else {
+ for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */
+ wc = (WCHAR)fno->altname[si];
+ if (wc == '.') lcf = NS_EXT;
+ if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20;
+ fno->fname[di] = (TCHAR)wc;
+ }
+ }
+ fno->fname[di] = 0; /* Terminate the LFN */
+ if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */
+ }
+
+#else /* Non-LFN configuration */
+ si = di = 0;
+ while (si < 11) { /* Copy name body and extension */
+ c = (TCHAR)dp->dir[si++];
+ if (c == ' ') continue; /* Skip padding spaces */
+ if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */
+ if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
+ fno->fname[di++] = c;
+ }
+ fno->fname[di] = 0;
+#endif
+
+ fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */
+ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */
+ fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */
+ fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+
+#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Pattern matching */
+/*-----------------------------------------------------------------------*/
+
+static DWORD get_achar ( /* Get a character and advances ptr */
+ const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
+)
+{
+ DWORD chr;
+
+
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */
+ chr = tchar2uni(ptr);
+ if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */
+ chr = ff_wtoupper(chr);
+
+#else /* ANSI/OEM input */
+ 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)) { /* Get DBC 2nd byte if needed */
+ chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0;
+ }
+#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;
+ DWORD 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 == 0 && 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 block */
+ 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 wc, *lfn;
+ DWORD uc;
+ UINT i, ni, si, di;
+ const TCHAR *p;
+
+
+ /* Create LFN into LFN working buffer */
+ p = *path; lfn = dp->obj.fs->lfnbuf; di = 0;
+ for (;;) {
+ uc = tchar2uni(&p); /* Get a character */
+ if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */
+ if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */
+ wc = (WCHAR)uc;
+ if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */
+ if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */
+ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */
+ lfn[di++] = wc; /* Store the Unicode character */
+ }
+ while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */
+ *path = p; /* Return pointer to the next segment */
+ cf = (wc < ' ') ? 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 */
+ wc = lfn[di - 1];
+ if (wc != ' ' && wc != '.') break;
+ di--;
+ }
+ lfn[di] = 0; /* LFN is created into the working buffer */
+ if (di == 0) return FR_INVALID_NAME; /* Reject null name */
+
+ /* Create SFN in directory form */
+ for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */
+ if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */
+ while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */
+
+ mem_set(dp->fn, ' ', 11);
+ i = b = 0; ni = 8;
+ for (;;) {
+ wc = lfn[si++]; /* Get an LFN character */
+ if (wc == 0) break; /* Break on end of the LFN */
+ if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */
+ cf |= NS_LOSS | NS_LFN;
+ continue;
+ }
+
+ if (i >= ni || si == di) { /* End of field? */
+ if (ni == 11) { /* Name extension overflow? */
+ cf |= NS_LOSS | NS_LFN;
+ break;
+ }
+ if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */
+ if (si > di) break; /* No name extension? */
+ si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */
+ continue;
+ }
+
+ if (wc >= 0x80) { /* Is this a non-ASCII character? */
+ cf |= NS_LFN; /* LFN entry needs to be created */
+#if FF_CODE_PAGE == 0
+ if (ExCvt) { /* At SBCS */
+ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
+ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
+ } else { /* At DBCS */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
+ }
+#elif FF_CODE_PAGE < 900 /* SBCS cfg */
+ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */
+ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */
+#else /* DBCS cfg */
+ wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */
+#endif
+ }
+
+ if (wc >= 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)(wc >> 8); /* Put 1st byte */
+ } else { /* SBC */
+ if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */
+ wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+ } else {
+ if (IsUpper(wc)) { /* ASCII upper case? */
+ b |= 2;
+ }
+ if (IsLower(wc)) { /* ASCII lower case? */
+ b |= 1; wc -= 0x20;
+ }
+ }
+ }
+ dp->fn[i++] = (BYTE)wc;
+ }
+
+ 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; /* Shift capital flags if no extension */
+ if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */
+ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */
+ if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */
+ if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */
+ }
+
+ dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */
+
+ 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++]; /* Get a byte */
+ 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 field overflow? */
+ if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */
+ i = 8; ni = 11; /* Enter file extension field */
+ continue;
+ }
+#if FF_CODE_PAGE == 0
+ if (ExCvt && c >= 0x80) { /* Is SBC extended character? */
+ c = ExCvt[c & 0x7F]; /* To upper SBC extended character */
+ }
+#elif FF_CODE_PAGE < 900
+ if (c >= 0x80) { /* Is SBC extended character? */
+ c = ExCvt[c & 0x7F]; /* 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;
+ init_alloc_info(fs, &dp->obj); /* Open next directory */
+ } 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 number or null pointer) */
+ const TCHAR** path /* Pointer to pointer to the path name */
+)
+{
+ const TCHAR *tp, *tt;
+ TCHAR tc;
+ int i, vol = -1;
+#if FF_STR_VOLUME_ID /* Find string volume ID */
+ const char *sp;
+ char c;
+#endif
+
+ tt = tp = *path;
+ if (!tp) return vol; /* Invalid path name? */
+ do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */
+
+ if (tc == ':') { /* DOS/Windows style volume ID? */
+ i = FF_VOLUMES;
+ if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */
+ i = (int)*tp - '0'; /* Get the LD number */
+ }
+#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */
+ else {
+ i = 0;
+ do {
+ sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
+ do { /* Compare the volume ID with path name */
+ c = *sp++; tc = *tp++;
+ if (IsLower(c)) c -= 0x20;
+ if (IsLower(tc)) tc -= 0x20;
+ } while (c && (TCHAR)c == tc);
+ } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */
+ }
+#endif
+ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
+ vol = i; /* Drive number */
+ *path = tt; /* Snip the drive prefix off */
+ }
+ return vol;
+ }
+#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */
+ if (*tp == '/') {
+ i = 0;
+ do {
+ sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */
+ do { /* Compare the volume ID with path name */
+ c = *sp++; tc = *(++tp);
+ if (IsLower(c)) c -= 0x20;
+ if (IsLower(tc)) tc -= 0x20;
+ } while (c && (TCHAR)c == tc);
+ } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */
+ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */
+ vol = i; /* Drive number */
+ *path = tp; /* Snip the drive prefix off */
+ return vol;
+ }
+ }
+#endif
+ /* No drive prefix is found */
+#if FF_FS_RPATH != 0
+ vol = CurrVol; /* Default drive is current drive */
+#else
+ vol = 0; /* Default drive is 0 */
+#endif
+ return vol; /* Return the default drive */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* 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 here regardless of the sector size) */
+
+#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] == 0xE8) { /* 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 */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Determine logical drive number and mount the volume if needed */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an 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 */
+ EFSPRINTF("WPEN1");
+ 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 */
+ EFSPRINTF("MDNR");
+ 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 */
+ EFSPRINTF("WPEN2");
+ 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 (MBR) and SFD (w/o partition). */
+ 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) {
+ EFSPRINTF("BRNL");
+ return FR_DISK_ERR; /* An error occured in the disk I/O layer */
+ }
+ if (fmt >= 2) {
+ EFSPRINTF("NOFAT");
+ 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;
+ DWORD so, cv, bcl;
+
+ 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) */
+ EFSPRINTF("EXSPS");
+ 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) {
+ EFSPRINTF("EXFNF");
+ 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);
+
+ /* Get bitmap location and check if it is contiguous (implementation assumption) */
+ so = i = 0;
+ for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */
+ if (i == 0) {
+ if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */
+ if (move_window(fs, clst2sect(fs, fs->dirbase) + so) != FR_OK) {
+ EFSPRINTF("EXBM1C");
+ return FR_DISK_ERR;
+ }
+ so++;
+ }
+ if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */
+ i = (i + SZDIRE) % SS(fs); /* Next entry */
+ }
+ bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */
+ if (bcl < 2 || bcl >= fs->n_fatent) {
+ EFSPRINTF("EXBMM");
+ return FR_NO_FILESYSTEM;
+ }
+ fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */
+ for (;;) { /* Check if bitmap is contiguous */
+ if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
+ cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4);
+ if (cv == 0xFFFFFFFF) break; /* Last link? */
+ if (cv != ++bcl) {
+ EFSPRINTF("EXBMM");
+ return FR_NO_FILESYSTEM; /* Fragmented? */
+ }
+ }
+
+#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)) {
+ EFSPRINTF("32SPS");
+ 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 buffer */
+#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) {
+ EFSPRINTF("IDRIVE!");
+ 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 number */
+ mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
+ 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;
+ init_alloc_info(fs, &fp->obj);
+ /* 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 && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */
+ res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+ fs->last_clst = fp->obj.sclust - 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 == 0) 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;
+ init_alloc_info(fs, &fp->obj);
+ } 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, sizeof fp->buf); /* 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;
+
+ UINT br_tmp;
+ if (!br)
+ br = &br_tmp;
+ *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) {
+ EFSPRINTF("FOV");
+ 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 btr bytes 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) {
+ EFSPRINTF("CCHK");
+ ABORT(fs, FR_INT_ERR);
+ }
+ if (clst == 0xFFFFFFFF) {
+ EFSPRINTF("DSKC");
+ 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) {
+ EFSPRINTF("RLIO");
+ 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) {
+ EFSPRINTF("RDC");
+ ABORT(fs, FR_DISK_ERR);
+ }
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+#endif
+ if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
+ EFSPRINTF("RSC");
+ 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_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Fast Read Aligned Sized File Without a Cache */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read_fast (
+ FIL* fp, /* Pointer to the file object */
+ const void* buff, /* Pointer to the data to be written */
+ UINT btr /* Number of bytes to read */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ UINT csize_bytes;
+ DWORD clst;
+ UINT count = 0;
+ FSIZE_t work_sector = 0;
+ FSIZE_t sector_base = 0;
+ BYTE *wbuff = (BYTE*)buff;
+
+ // TODO support sector reading inside a cluster
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ LEAVE_FF(fs, res); /* Check validity */
+ }
+
+ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+ FSIZE_t remain = fp->obj.objsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+
+ csize_bytes = fs->csize * SS(fs);
+
+ if (!fp->fptr) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow from the origin */
+ } else {
+ if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
+ }
+ if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst; /* Set working cluster */
+
+ sector_base = clst2sect(fs, fp->clust);
+ count += fs->csize;
+ btr -= csize_bytes;
+ fp->fptr += csize_bytes;
+
+ while (btr) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+
+ if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst;
+
+ work_sector = clst2sect(fs, fp->clust);
+ if ((work_sector - sector_base) == count) count += fs->csize;
+ else {
+ if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ wbuff += count * SS(fs);
+
+ sector_base = work_sector;
+ count = fs->csize;
+ }
+
+ fp->fptr += MIN(btr, csize_bytes);
+ btr -= MIN(btr, csize_bytes);
+
+ // TODO: what about if data is smaller than cluster?
+ // Must read-write back that cluster.
+
+ if (!btr) { /* Final cluster/sectors read. */
+ if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ }
+ }
+
+ LEAVE_FF(fs, FR_OK);
+}
+#endif
+
+
+
+
+#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;
+
+ UINT bw_tmp;
+ if (!bw)
+ bw = &bw_tmp;
+ *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) {
+ EFSPRINTF("FOV");
+ 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) {
+ EFSPRINTF("DSKFULL");
+ break; /* Could not allocate a new cluster (disk full) */
+ }
+ if (clst == 1) {
+ EFSPRINTF("CCHK");
+ ABORT(fs, FR_INT_ERR);
+ }
+ if (clst == 0xFFFFFFFF) {
+ EFSPRINTF("DERR");
+ 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) {
+ EFSPRINTF("WLIO");
+ 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);
+}
+
+
+
+
+#if FF_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Fast Write Aligned Sized File Without a Cache */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write_fast (
+ FIL* fp, /* Pointer to the file object */
+ const void* buff, /* Pointer to the data to be written */
+ UINT btw /* Number of bytes to write */
+)
+{
+ FRESULT res;
+ FATFS *fs;
+ UINT csize_bytes;
+ DWORD clst;
+ UINT count = 0;
+ FSIZE_t work_sector = 0;
+ FSIZE_t sector_base = 0;
+ const BYTE *wbuff = (const BYTE*)buff;
+
+ // TODO support sector writing inside a cluster
+
+ res = validate(&fp->obj, &fs); /* Check validity of the file object */
+ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
+ EFSPRINTF("FOV");
+ 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);
+ }
+
+ csize_bytes = fs->csize * SS(fs);
+
+ if (!fp->fptr) { /* On the top of the file? */
+ clst = fp->obj.sclust; /* Follow from the origin */
+ } else {
+ if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+ else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
+ }
+
+ if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst; /* Set working cluster */
+
+ sector_base = clst2sect(fs, fp->clust);
+ count += fs->csize;
+ btw -= csize_bytes;
+ fp->fptr += csize_bytes;
+
+ while (btw) {
+ clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
+
+ if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
+ else if (clst == 0xFFFFFFFF) { EFSPRINTF("DERR"); ABORT(fs, FR_DISK_ERR); }
+
+ fp->clust = clst;
+
+ work_sector = clst2sect(fs, fp->clust);
+ if ((work_sector - sector_base) == count) count += fs->csize;
+ else {
+ if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ wbuff += count * SS(fs);
+
+ sector_base = work_sector;
+ count = fs->csize;
+ }
+
+ fp->fptr += MIN(btw, csize_bytes);
+ btw -= MIN(btw, csize_bytes);
+
+ // what about if data is smaller than cluster?
+ // Probably must read-write back that cluster.
+ if (!btw) { /* Final cluster/sectors write. */
+ if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
+ fp->flag &= (BYTE)~FA_DIRTY;
+ }
+ }
+
+ fp->flag |= FA_MODIFIED; /* Set file change flag */
+
+ LEAVE_FF(fs, FR_OK);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* 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 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chdrive (
+ const TCHAR* path /* Drive number to set */
+)
+{
+ 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;
+}
+
+
+
+FRESULT f_chdir (
+ const TCHAR* path /* Pointer to the directory path */
+)
+{
+#if FF_STR_VOLUME_ID == 2
+ UINT i;
+#endif
+ 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) { /* Is it the start directory itself? */
+ fs->cdir = dj.obj.sclust;
+#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;
+#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */
+ if (res == FR_OK) {
+ for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */
+ CurrVol = (BYTE)i;
+ }
+#endif
+ }
+
+ LEAVE_FF(fs, res);
+}
+
+
+#if FF_FS_RPATH >= 2
+FRESULT f_getcwd (
+ TCHAR* buff, /* Pointer to the directory path */
+ UINT len /* Size of buff in unit of TCHAR */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ UINT i, n;
+ DWORD ccl;
+ TCHAR *tp = buff;
+#if FF_VOLUMES >= 2
+ UINT vl;
+#if FF_STR_VOLUME_ID
+ const char *vp;
+#endif
+#endif
+ FILINFO fno;
+ DEF_NAMBUF
+
+
+ /* Get logical drive */
+ buff[0] = 0; /* Set null string to get current volume */
+ res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
+ if (res == FR_OK) {
+ dj.obj.fs = fs;
+ INIT_NAMBUF(fs);
+
+ /* Follow parent directories and create the path */
+ 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_FILE(&dj);
+ 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++) ; /* Name length */
+ if (i < n + 1) { /* Insufficient space to store the path name? */
+ res = FR_NOT_ENOUGH_CORE; break;
+ }
+ while (n) buff[--i] = fno.fname[--n]; /* Stack the name */
+ buff[--i] = '/';
+ }
+ }
+ if (res == FR_OK) {
+ if (i == len) buff[--i] = '/'; /* Is it the root-directory? */
+#if FF_VOLUMES >= 2 /* Put drive prefix */
+ vl = 0;
+#if FF_STR_VOLUME_ID >= 1 /* String volume ID */
+ for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
+ if (i >= n + 2) {
+ if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
+ for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
+ if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
+ vl++;
+ }
+#else /* Numeric volume ID */
+ if (i >= 3) {
+ *tp++ = (TCHAR)'0' + CurrVol;
+ *tp++ = (TCHAR)':';
+ vl = 2;
+ }
+#endif
+ if (vl == 0) res = FR_NOT_ENOUGH_CORE;
+#endif
+ /* Add current directory path */
+ if (res == FR_OK) {
+ do *tp++ = buff[i++]; while (i < len); /* Copy stacked path string */
+ }
+ }
+ FREE_NAMBUF();
+ }
+
+ *tp = 0;
+ 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_FASTFS && FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* Seek File Read/Write Pointer */
+/*-----------------------------------------------------------------------*/
+
+DWORD *f_expand_cltbl (
+ FIL* fp, /* Pointer to the file object */
+ UINT tblsz, /* Size of table */
+ FSIZE_t ofs /* File pointer from top of file */
+)
+{
+ if (fp->flag & FA_WRITE) f_lseek(fp, ofs); /* Expand file if write is enabled */
+ if (!fp->cltbl) { /* Allocate memory for cluster link table */
+ fp->cltbl = (DWORD *)ff_memalloc(tblsz);
+ fp->cltbl[0] = tblsz;
+ }
+ if (f_lseek(fp, CREATE_LINKMAP)) { /* Create cluster link table */
+ ff_memfree(fp->cltbl);
+ fp->cltbl = NULL;
+ EFSPRINTF("CLTBLSZ");
+ return NULL;
+ }
+ f_lseek(fp, 0);
+
+ return fp->cltbl;
+}
+#endif
+
+
+
+
+#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;
+ init_alloc_info(fs, &dp->obj); /* Get object allocation info */
+ } 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_FILE(dp); /* 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, /* 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) {
+ if (fatfs) *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->bitbase; /* Bitmap sector */
+ 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) {
+ init_alloc_info(fs, &obj);
+ dclst = obj.sclust;
+ } 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_FILE(&sdj); /* Test if the directory is empty */
+ if (res == FR_OK) res = FR_OK; // Not empty? Copyright(c) 2019 Storm, if (res == FR_OK) res = FR_DENIED; // Not empty? //geändert in FR_OK
+ 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 != 0) { /* 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;
+ FFOBJID sobj;
+ FATFS *fs;
+ DWORD dcl, pcl, tm;
+ 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) res = FR_EXIST; /* Name collision? */
+ if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */
+ res = FR_INVALID_NAME;
+ }
+ if (res == FR_NO_FILE) { /* It is clear to create a new directory */
+ sobj.fs = fs; /* New object id to create a new chain */
+ dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */
+ res = FR_OK;
+ if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */
+ if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */
+ if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */
+ tm = GET_FATTIME();
+ if (res == FR_OK) {
+ res = dir_clear(fs, dcl); /* Clean up the new table */
+ if (res == FR_OK) {
+ if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */
+ mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */
+ fs->win[DIR_Name] = '.';
+ fs->win[DIR_Attr] = AM_DIR;
+ st_dword(fs->win + DIR_ModTime, tm);
+ st_clust(fs, fs->win, dcl);
+ mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */
+ fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
+ st_clust(fs, fs->win + SZDIRE, pcl);
+ fs->wflag = 1;
+ }
+ res = dir_register(&dj); /* Register the object to the parent 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)fs->csize * SS(fs)); /* File size needs to be valid */
+ st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs));
+ fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */
+ fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */
+ res = store_xdir(&dj);
+ } else
+#endif
+ {
+ st_dword(dj.dir + DIR_ModTime, tm); /* Created time */
+ st_clust(fs, dj.dir, dcl); /* Table start cluster */
+ dj.dir[DIR_Attr] = AM_DIR; /* Attribute */
+ fs->wflag = 1;
+ }
+ if (res == FR_OK) {
+ res = sync_fs(fs);
+ }
+ } else {
+ remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */
+ }
+ }
+ 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, /* Logical drive number */
+ TCHAR* label, /* Buffer to store the volume label */
+ DWORD* vsn /* Variable to store the volume serial number */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ UINT si, di;
+ WCHAR wc;
+
+ /* 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_LABEL(&dj); /* Find a volume label entry */
+ if (res == FR_OK) {
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) {
+ WCHAR hs;
+
+ for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */
+ wc = ld_word(dj.dir + XDIR_Label + si * 2);
+ if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */
+ hs = wc; continue;
+ }
+ wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4);
+ if (wc == 0) { di = 0; break; }
+ di += wc;
+ hs = 0;
+ }
+ if (hs != 0) di = 0; /* Broken surrogate pair? */
+ label[di] = 0;
+ } else
+#endif
+ {
+ si = di = 0; /* Extract volume label from AM_VOL entry */
+ while (si < 11) {
+ wc = dj.dir[si++];
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */
+ if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */
+ wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */
+ if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */
+ if (wc == 0) { di = 0; break; }
+ di += wc;
+#else /* ANSI/OEM output */
+ label[di++] = (TCHAR)wc;
+#endif
+ }
+ 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 /* Volume label to set with heading logical drive number */
+)
+{
+ FRESULT res;
+ DIR dj;
+ FATFS *fs;
+ BYTE dirvn[22];
+ UINT di;
+ WCHAR wc;
+ static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */
+#if FF_USE_LFN
+ DWORD dc;
+#endif
+
+ /* Get logical drive */
+ res = find_volume(&label, &fs, FA_WRITE);
+ if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_FS_EXFAT
+ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
+ mem_set(dirvn, 0, 22);
+ di = 0;
+ while ((UINT)*label >= ' ') { /* Create volume label */
+ dc = tchar2uni(&label); /* Get a Unicode character */
+ if (dc >= 0x10000) {
+ if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */
+ dc = 0;
+ } else {
+ st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++;
+ }
+ }
+ if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */
+ LEAVE_FF(fs, FR_INVALID_NAME);
+ }
+ st_word(dirvn + di * 2, (WCHAR)dc); di++;
+ }
+ } else
+#endif
+ { /* On the FAT/FAT32 volume */
+ mem_set(dirvn, ' ', 11);
+ di = 0;
+ while ((UINT)*label >= ' ') { /* Create volume label */
+#if FF_USE_LFN
+ dc = tchar2uni(&label);
+ wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0;
+#else /* ANSI/OEM input */
+ wc = (BYTE)*label++;
+ if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0;
+ if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */
+#if FF_CODE_PAGE == 0
+ if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
+#elif FF_CODE_PAGE < 900
+ if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
+#endif
+#endif
+ if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */
+ LEAVE_FF(fs, FR_INVALID_NAME);
+ }
+ if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8);
+ dirvn[di++] = (BYTE)wc;
+ }
+ if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */
+ while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */
+ }
+
+ /* Set volume label */
+ dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */
+ res = dir_sdi(&dj, 0);
+ if (res == FR_OK) {
+ res = DIR_READ_LABEL(&dj); /* Get volume label entry */
+ if (res == FR_OK) {
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+ dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */
+ mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+ } else {
+ if (di != 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 (di != 0) { /* Create a volume label entry */
+ res = dir_alloc(&dj, 1); /* Allocate an entry */
+ if (res == FR_OK) {
+ mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */
+ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+ dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */
+ dj.dir[XDIR_NumLabel] = (BYTE)di;
+ mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+ } 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 (null: use heap memory) */
+ UINT len /* Size of working buffer [byte] */
+)
+{
+ const UINT n_fats = 2; /* 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; /* Sector size */
+ 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 if mounted */
+ 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 */
+#if FF_USE_LFN == 3
+ if (!work) { /* Use heap memory for working buffer */
+ for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && (buf = ff_memalloc(szb_buf)) == 0; szb_buf /= 2) ;
+ sz_buf = szb_buf / ss; /* Size of working buffer (sector) */
+ } else
+#endif
+ {
+ 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 (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE;
+
+ /* 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) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */
+ if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */
+ pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
+ if (pte[PTE_System] == 0) LEAVE_MKFS(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) LEAVE_MKFS(FR_DISK_ERR);
+ b_vol = (opt & FM_SFD) ? 0 : 32768; /* Volume start sector. Align to 16MB */
+ if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED);
+ sz_vol -= b_vol; /* Volume size */
+ }
+ if (sz_vol < 128) LEAVE_MKFS(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) LEAVE_MKFS(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)) LEAVE_MKFS(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) LEAVE_MKFS(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 - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */
+ n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */
+ if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
+ if (n_clst > MAX_EXFAT) LEAVE_MKFS(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 = 0; si = 0; i = 0; j = 0; szb_case = 0;
+ do {
+ switch (st) {
+ case 0:
+ ch = (WCHAR)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 += (WCHAR)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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(FR_DISK_ERR);
+ sect += n; nsect -= n;
+ } while (nsect);
+
+ /* Initialize the root directory */
+ mem_set(buf, 0, szb_buf);
+ buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry */
+ buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */
+ st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */
+ st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
+ buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
+ st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
+ st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
+ st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
+ 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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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 */
+ if (n % n_fats) { /* Adjust fractional error if needed */
+ n--; sz_rsv++; b_fat++;
+ }
+ 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) LEAVE_MKFS(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 */
+ LEAVE_MKFS(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 */
+ LEAVE_MKFS(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 */
+ LEAVE_MKFS(FR_MKFS_ABORTED);
+ }
+ }
+ if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(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, "SWITCH SD " "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, "SWITCH SD " "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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) LEAVE_MKFS(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) & 0xC0) | 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) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */
+ }
+ }
+
+ if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+
+ LEAVE_MKFS(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 (null: use heap memory) */
+)
+{
+ 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;
+ FRESULT res;
+
+
+ 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;
+
+ buf = (BYTE*)work;
+#if FF_USE_LFN == 3
+ if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
+#endif
+ if (!buf) return FR_NOT_ENOUGH_CORE;
+
+ /* 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 = (BYTE)(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) LEAVE_MKFS(FR_INVALID_PARAMETER);
+
+ /* Set partition table */
+ p[1] = s_hd; /* Start head */
+ p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 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) & 0xC0) | 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); /* MBR signature (always at offset 510) */
+
+ /* Write it to the MBR */
+ res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+ LEAVE_MKFS(res);
+}
+
+#endif /* FF_MULTI_PARTITION */
+#endif /* FF_USE_MKFS && !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_STRFUNC
+#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
+#error Wrong FF_STRF_ENCODE setting
+#endif
+/*-----------------------------------------------------------------------*/
+/* Get a String from the File */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+ TCHAR* buff, /* Pointer to the string buffer to read */
+ int len, /* Size of string buffer (items) */
+ FIL* fp /* Pointer to the file object */
+)
+{
+ int nc = 0;
+ TCHAR *p = buff;
+ BYTE s[4];
+ UINT rc;
+ DWORD dc;
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
+ WCHAR wc;
+#endif
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
+ UINT ct;
+#endif
+
+#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */
+ /* Make a room for the character and terminator */
+ if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2;
+ if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4;
+ if (FF_LFN_UNICODE == 3) len -= 1;
+ while (nc < len) {
+#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ wc = s[0];
+ if (dbc_1st((BYTE)wc)) {
+ f_read(fp, s, 1, &rc);
+ if (rc != 1 || !dbc_2nd(s[0])) continue;
+ wc = wc << 8 | s[0];
+ }
+ dc = ff_oem2uni(wc, CODEPAGE);
+ if (dc == 0) continue;
+#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */
+ f_read(fp, s, 2, &rc);
+ if (rc != 2) break;
+ dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (IsSurrogateL(dc)) continue;
+ if (IsSurrogateH(dc)) {
+ f_read(fp, s, 2, &rc);
+ if (rc != 2) break;
+ wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+ if (!IsSurrogateL(wc)) continue;
+ dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF);
+ }
+#else /* Read a character in UTF-8 */
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ dc = s[0];
+ if (dc >= 0x80) { /* Multi-byte character? */
+ ct = 0;
+ if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte? */
+ if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte? */
+ if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte? */
+ if (ct == 0) continue;
+ f_read(fp, s, ct, &rc); /* Get trailing bytes */
+ if (rc != ct) break;
+ rc = 0;
+ do { /* Merge trailing bytes */
+ if ((s[rc] & 0xC0) != 0x80) break;
+ dc = dc << 6 | (s[rc] & 0x3F);
+ } while (++rc < ct);
+ if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */
+ }
+#endif
+ if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */
+#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */
+ if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */
+ *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */
+ dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */
+ }
+ *p++ = (TCHAR)dc; nc++;
+ if (dc == '\n') break; /* End of line? */
+#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */
+ if (dc < 0x80) { /* 1-byte */
+ *p++ = (TCHAR)dc;
+ nc++;
+ if (dc == '\n') break; /* End of line? */
+ } else {
+ if (dc < 0x800) { /* 2-byte */
+ *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 2;
+ } else {
+ if (dc < 0x10000) { /* 3-byte */
+ *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
+ *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 3;
+ } else { /* 4-byte */
+ *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
+ *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+ *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+ nc += 4;
+ }
+ }
+ }
+#endif
+ }
+
+#else /* Byte-by-byte without any conversion (ANSI/OEM API) */
+ len -= 1; /* Make a room for the terminator */
+ while (nc < len) {
+ f_read(fp, s, 1, &rc);
+ if (rc != 1) break;
+ dc = s[0];
+ if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
+ *p++ = (TCHAR)dc; nc++;
+ if (dc == '\n') break;
+ }
+#endif
+
+ *p = 0; /* Terminate the string */
+ return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */
+}
+
+
+
+
+#if !FF_FS_READONLY
+#include
+/*-----------------------------------------------------------------------*/
+/* Put a Character to the File */
+/*-----------------------------------------------------------------------*/
+
+typedef struct { /* Putchar output buffer and work area */
+ FIL *fp; /* Ptr to the writing file */
+ int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */
+#if FF_USE_LFN && FF_LFN_UNICODE == 1
+ WCHAR hs;
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2
+ BYTE bs[4];
+ UINT wi, ct;
+#endif
+ BYTE buf[64]; /* Write buffer */
+} putbuff;
+
+
+static void putc_bfd ( /* Buffered write with code conversion */
+ putbuff* pb,
+ TCHAR c
+)
+{
+ UINT n;
+ int i, nc;
+#if FF_USE_LFN && FF_LFN_UNICODE
+ WCHAR hs, wc;
+#if FF_LFN_UNICODE == 2
+ DWORD dc;
+ TCHAR *tp;
+#endif
+#endif
+
+ 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;
+ nc = pb->nchr; /* Write unit counter */
+
+#if FF_USE_LFN && FF_LFN_UNICODE
+#if FF_LFN_UNICODE == 1 /* UTF-16 input */
+ if (IsSurrogateH(c)) {
+ pb->hs = c; return;
+ }
+ hs = pb->hs; pb->hs = 0;
+ if (hs != 0) {
+ if (!IsSurrogateL(c)) hs = 0;
+ } else {
+ if (IsSurrogateL(c)) return;
+ }
+ wc = c;
+#elif FF_LFN_UNICODE == 2 /* UTF-8 input */
+ for (;;) {
+ if (pb->ct == 0) { /* Out of multi-byte sequence? */
+ pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */
+ if ((BYTE)c < 0x80) break; /* 1-byte? */
+ if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */
+ if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */
+ if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */
+ return;
+ } else { /* In the multi-byte sequence */
+ if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */
+ pb->ct = 0; continue;
+ }
+ pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */
+ if (--pb->ct == 0) break; /* End of multi-byte sequence? */
+ return;
+ }
+ }
+ tp = (TCHAR*)pb->bs;
+ dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
+ if (dc == 0xFFFFFFFF) return;
+ wc = (WCHAR)dc;
+ hs = (WCHAR)(dc >> 16);
+#elif FF_LFN_UNICODE == 3 /* UTF-32 input */
+ if (IsSurrogate(c) || c >= 0x110000) return;
+ if (c >= 0x10000) {
+ hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */
+ wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */
+ } else {
+ hs = 0;
+ wc = (WCHAR)c;
+ }
+#endif
+
+#if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
+ if (hs != 0) {
+ st_word(&pb->buf[i], hs);
+ i += 2;
+ nc++;
+ }
+ st_word(&pb->buf[i], wc);
+ i += 2;
+#elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
+ if (hs != 0) {
+ pb->buf[i++] = (BYTE)(hs >> 8);
+ pb->buf[i++] = (BYTE)hs;
+ nc++;
+ }
+ pb->buf[i++] = (BYTE)(wc >> 8);
+ pb->buf[i++] = (BYTE)wc;
+#elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */
+ if (hs != 0) { /* 4-byte */
+ nc += 3;
+ hs = (hs & 0x3FF) + 0x40;
+ pb->buf[i++] = (BYTE)(0xF0 | hs >> 8);
+ pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F));
+ pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F));
+ pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+ } else {
+ if (wc < 0x80) { /* 1-byte */
+ pb->buf[i++] = (BYTE)wc;
+ } else {
+ if (wc < 0x800) { /* 2-byte */
+ nc += 1;
+ pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
+ } else { /* 3-byte */
+ nc += 2;
+ pb->buf[i++] = (BYTE)(0xE0 | wc >> 12);
+ pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F));
+ }
+ pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+ }
+ }
+#else /* Write it in ANSI/OEM */
+ if (hs != 0) return;
+ wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */
+ if (wc == 0) return;
+ if (wc >= 0x100) {
+ pb->buf[i++] = (BYTE)(wc >> 8); nc++;
+ }
+ pb->buf[i++] = (BYTE)wc;
+#endif
+
+#else /* ANSI/OEM input (without re-encode) */
+ pb->buf[i++] = (BYTE)c;
+#endif
+
+ if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */
+ f_write(pb->fp, pb->buf, (UINT)i, &n);
+ i = (n == (UINT)i) ? 0 : -1;
+ }
+ pb->idx = i;
+ pb->nchr = nc + 1;
+}
+
+
+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
+)
+{
+ mem_set(pb, 0, sizeof (putbuff));
+ pb->fp = fp;
+}
+
+
+
+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++;
+ }
+ }
+ if (c == '*') { /* Minimum width by argument */
+ w = va_arg(arp, int);
+ c = *fmt++;
+ } else {
+ while (IsDigit(c)) { /* Minimum width */
+ w = w * 10 + c - '0';
+ c = *fmt++;
+ }
+ }
+ if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */
+ f |= 4; c = *fmt++;
+ }
+ if (c == 0) break;
+ d = c;
+ if (IsLower(d)) d -= 0x20;
+ switch (d) { /* Atgument type is... */
+ case 'S' : /* String */
+ p = va_arg(arp, TCHAR*);
+ for (j = 0; p[j]; j++) ;
+ if (!(f & 2)) { /* Right padded */
+ while (j++ < w) putc_bfd(&pb, ' ') ;
+ }
+ while (*p) putc_bfd(&pb, *p++) ; /* String body */
+ while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
+ continue;
+
+ case 'C' : /* Character */
+ putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+
+ case 'B' : /* Unsigned binary */
+ r = 2; break;
+
+ case 'O' : /* Unsigned octal */
+ r = 8; break;
+
+ case 'D' : /* Signed decimal */
+ case 'U' : /* Unsigned decimal */
+ r = 10; break;
+
+ case 'X' : /* Unsigned 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; /* Not found? */
+
+ 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 */
diff --git a/bdk/libs/fatfs/ff.h b/bdk/libs/fatfs/ff.h
new file mode 100644
index 00000000..a83cf63d
--- /dev/null
+++ b/bdk/libs/fatfs/ff.h
@@ -0,0 +1,390 @@
+/*----------------------------------------------------------------------------/
+/ FatFs - Generic FAT Filesystem module R0.13c /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2018, 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.
+/
+/----------------------------------------------------------------------------*/
+
+
+#ifndef FF_DEFINED
+#define FF_DEFINED 86604 /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include /* Basic integer types */
+#include /* FatFs configuration options */
+
+#if FF_DEFINED != FFCONF_DEF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if FF_MULTI_PARTITION /* Multiple partition configuration */
+typedef struct {
+ BYTE pd; /* Physical drive number */
+ BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifndef FF_VOLUME_STRS
+extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
+#endif
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#ifndef _INC_TCHAR
+#define _INC_TCHAR
+
+#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
+typedef char TCHAR;
+#define _T(x) u8 ## x
+#define _TEXT(x) u8 ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
+typedef DWORD TCHAR;
+#define _T(x) U ## x
+#define _TEXT(x) U ## x
+#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
+#error Wrong FF_LFN_UNICODE setting
+#else /* ANSI/OEM code in SBCS/DBCS */
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* Type of file size variables */
+
+#if FF_FS_EXFAT
+typedef QWORD FSIZE_t;
+#else
+typedef DWORD FSIZE_t;
+#endif
+
+
+
+/* Filesystem object structure (FATFS) */
+
+typedef struct {
+ BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
+ BYTE fs_type; /* Filesystem type (0:not mounted) */
+ BYTE pdrv; /* Associated physical drive */
+ BYTE n_fats; /* Number of FATs (1 or 2) */
+ BYTE wflag; /* win[] flag (b0:dirty) */
+ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
+ WORD id; /* Volume mount ID */
+ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
+ WORD csize; /* Cluster size [sectors] */
+#if FF_MAX_SS != FF_MIN_SS
+ WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
+#endif
+#if FF_USE_LFN
+ WCHAR* lfnbuf; /* LFN working buffer */
+#endif
+#if FF_FS_EXFAT
+ BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
+#endif
+#if FF_FS_REENTRANT
+ FF_SYNC_t sobj; /* Identifier of sync object */
+#endif
+#if !FF_FS_READONLY
+ DWORD last_clst; /* Last allocated cluster */
+ DWORD free_clst; /* Number of free clusters */
+#endif
+#if FF_FS_RPATH
+ DWORD cdir; /* Current directory start cluster (0:root) */
+#if FF_FS_EXFAT
+ DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
+ DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
+ DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
+#endif
+#endif
+ DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
+ DWORD fsize; /* Size of an FAT [sectors] */
+ DWORD volbase; /* Volume base sector */
+ DWORD fatbase; /* FAT base sector */
+ DWORD dirbase; /* Root directory base sector/cluster */
+ DWORD database; /* Data base sector */
+#if FF_FS_EXFAT
+ DWORD bitbase; /* Allocation bitmap base sector */
+#endif
+ DWORD winsect; /* Current sector appearing in the win[] */
+} FATFS;
+
+
+
+/* Object ID and allocation information (FFOBJID) */
+
+typedef struct {
+ FATFS* fs; /* Pointer to the hosting volume of this object */
+ WORD id; /* Hosting volume mount ID */
+ BYTE attr; /* Object attribute */
+ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
+ DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
+ FSIZE_t objsize; /* Object size (valid when sclust != 0) */
+#if FF_FS_EXFAT
+ DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
+ DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
+ DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
+ DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
+ DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
+#endif
+#if FF_FS_LOCK
+ UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
+#endif
+} FFOBJID;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+#if !FF_FS_TINY
+ BYTE buf[FF_MAX_SS]; /* File private data read/write window */
+#endif
+ FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
+ BYTE flag; /* File status flags */
+ BYTE err; /* Abort flag (error code) */
+ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
+ DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
+ DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
+#if !FF_FS_READONLY
+ DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
+ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
+#endif
+#if FF_USE_FASTSEEK
+ DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+ FFOBJID obj; /* Object identifier */
+ DWORD dptr; /* Current read/write offset */
+ DWORD clust; /* Current cluster */
+ DWORD sect; /* Current sector (0:Read operation has terminated) */
+ BYTE* dir; /* Pointer to the directory item in the win[] */
+ BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
+#if FF_USE_LFN
+ DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
+#endif
+#if FF_USE_FIND
+ const TCHAR* pat; /* Pointer to the name matching pattern */
+#endif
+} DIR;
+
+
+
+/* File information structure (FILINFO) */
+
+typedef struct {
+ FSIZE_t fsize; /* File size */
+ WORD fdate; /* Modified date */
+ WORD ftime; /* Modified time */
+ BYTE fattrib; /* File attribute */
+#if FF_USE_LFN
+ TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
+ TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
+#else
+ TCHAR fname[12 + 1]; /* File name */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+ FR_OK = 0, /* (0) Succeeded */
+ FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
+ FR_INT_ERR, /* (2) Assertion failed */
+ FR_NOT_READY, /* (3) The physical drive cannot work */
+ FR_NO_FILE, /* (4) Could not find the file */
+ FR_NO_PATH, /* (5) Could not find the path */
+ FR_INVALID_NAME, /* (6) The path name format is invalid */
+ FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
+ FR_EXIST, /* (8) Access denied due to prohibited access */
+ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
+ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
+ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
+ FR_NOT_ENABLED, /* (12) The volume has no work area */
+ FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
+ FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
+ FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
+ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
+ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
+ FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
+#if FF_FASTFS
+ FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
+ FR_CLTBL_NO_INIT /* (20) The cluster table for fast seek/read/write was not created */
+#else
+ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
+#endif
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface */
+
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
+FRESULT f_close (FIL* fp); /* Close an open file object */
+FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
+FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
+FRESULT f_read_fast (FIL* fp, const void* buff, UINT btr); /* Fast read data from the file */
+FRESULT f_write_fast (FIL* fp, const void* buff, UINT btw); /* Fast write data to the file */
+FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
+FRESULT f_truncate (FIL* fp); /* Truncate the file */
+FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
+FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
+FRESULT f_closedir (DIR* dp); /* Close an open directory */
+FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
+FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
+FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
+FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
+FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
+FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
+FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
+FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
+FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
+FRESULT f_chdir (const TCHAR* path); /* Change current directory */
+FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
+FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
+FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
+FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
+FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
+FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
+DWORD *f_expand_cltbl (FIL* fp, UINT tblsz, FSIZE_t ofs); /* Expand file and populate cluster table */
+FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
+FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
+FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
+FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
+FRESULT f_setcp (WORD cp); /* Set current code page */
+int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
+int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
+int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
+
+#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
+#define f_error(fp) ((fp)->err)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->obj.objsize)
+#define f_rewind(fp) f_lseek((fp), 0)
+#define f_rewinddir(dp) f_readdir((dp), 0)
+#define f_rmdir(path) f_unlink(path)
+#define f_unmount(path) f_mount(0, path, 0)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions */
+
+/* RTC function */
+#if !FF_FS_READONLY && !FF_FS_NORTC
+DWORD get_fattime (void);
+#endif
+
+/* LFN support functions */
+#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
+WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
+WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
+DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
+#endif
+#if FF_USE_LFN == 3 /* Dynamic memory allocation */
+void* ff_memalloc (UINT msize); /* Allocate memory block */
+void ff_memfree (void* mblock); /* Free memory block */
+#endif
+
+/* Sync functions */
+#if FF_FS_REENTRANT
+int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
+int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
+void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
+int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address */
+
+
+/* File access mode and open method flags (3rd argument of f_open) */
+#define FA_READ 0x01
+#define FA_WRITE 0x02
+#define FA_OPEN_EXISTING 0x00
+#define FA_CREATE_NEW 0x04
+#define FA_CREATE_ALWAYS 0x08
+#define FA_OPEN_ALWAYS 0x10
+#define FA_OPEN_APPEND 0x30
+
+/* Fast seek controls (2nd argument of f_lseek) */
+#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
+
+/* Format options (2nd argument of f_mkfs) */
+#define FM_FAT 0x01
+#define FM_FAT32 0x02
+#define FM_EXFAT 0x04
+#define FM_ANY 0x07
+#define FM_SFD 0x08
+
+/* Filesystem type (FATFS.fs_type) */
+#define FS_FAT12 1
+#define FS_FAT16 2
+#define FS_FAT32 3
+#define FS_EXFAT 4
+
+/* File attribute bits for directory entry (FILINFO.fattrib) */
+#define AM_RDO 0x01 /* Read only */
+#define AM_HID 0x02 /* Hidden */
+#define AM_SYS 0x04 /* System */
+#define AM_VOL 0x08 /* Volume */
+#define AM_DIR 0x10 /* Directory */
+#define AM_ARC 0x20 /* Archive */
+#define AM_DEV 0x40 /* Device */
+#define AM_RVD 0x80 /* Reserved */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FF_DEFINED */
diff --git a/bdk/libs/fatfs/ffunicode.c b/bdk/libs/fatfs/ffunicode.c
new file mode 100644
index 00000000..9f039637
--- /dev/null
+++ b/bdk/libs/fatfs/ffunicode.c
@@ -0,0 +1,625 @@
+/*------------------------------------------------------------------------*/
+/* Unicode handling functions for FatFs R0.13c */
+/*------------------------------------------------------------------------*/
+/* This module will occupy a huge memory in the .const section when the /
+/ FatFs is configured for LFN with DBCS. If the system has any Unicode /
+/ utilitiy for the code conversion, this module should be modified to use /
+/ that function to avoid silly memory consumption. /
+/-------------------------------------------------------------------------*/
+/*
+/ Copyright (C) 2018, 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"
+
+#if FF_USE_LFN /* This module will be blanked at non-LFN configuration */
+
+#if FF_DEFINED != 86604 /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+#define MERGE2(a, b) a ## b
+#define CVTBL(tbl, cp) MERGE2(tbl, cp)
+
+/*------------------------------------------------------------------------*/
+/* Code Conversion Tables */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0
+static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0
+static const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
+ 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+ 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+ 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0
+static const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
+ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+ 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+ 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+ 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0
+static const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0
+static const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
+ 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+ 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+ 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+ 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0
+static const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0
+static const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+ 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+ 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0
+static const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
+ 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+ 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+ 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+ 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+ 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0
+static const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0
+static const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
+ 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0
+static const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0
+static const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0
+static const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
+ 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
+ 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0
+static const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
+ 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
+ 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
+ 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
+ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
+ 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
+ 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
+ 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
+ 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
+};
+#endif
+#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0
+static const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0
+static const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+#endif
+#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0
+static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
+ 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
+ 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
+ 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
+ 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
+ 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
+ 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
+};
+#endif
+
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration */
+/* SBCS fixed code page */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ WCHAR c = 0;
+ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
+ for (c = 0; c < 0x80 && uni != p[c]; c++) ;
+ c = (c + 0x80) & 0xFF;
+ }
+ }
+
+ return c;
+}
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ WCHAR c = 0;
+ const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
+ if (oem < 0x100) c = p[oem - 0x80];
+ }
+ }
+
+ return c;
+}
+
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration */
+/* DBCS fixed code page */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE >= 900
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0, uc;
+ UINT i = 0, n, li, hi;
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
+ uc = (WCHAR)uni;
+ p = CVTBL(uni2oem, FF_CODE_PAGE);
+ hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1;
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (uc == p[i * 2]) break;
+ if (uc > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+
+ return c;
+}
+
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0;
+ UINT i = 0, n, li, hi;
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ if (cp == FF_CODE_PAGE) { /* Is it valid code page? */
+ p = CVTBL(oem2uni, FF_CODE_PAGE);
+ hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1;
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (oem == p[i * 2]) break;
+ if (oem > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+
+ return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for dynamic code page configuration */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 0
+
+static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0};
+static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
+
+
+WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
+ DWORD uni, /* UTF-16 encoded character to be converted */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0, uc;
+ UINT i, n, li, hi;
+
+
+ if (uni < 0x80) { /* ASCII? */
+ c = (WCHAR)uni;
+
+ } else { /* Non-ASCII */
+ if (uni < 0x10000) { /* Is it in BMP? */
+ uc = (WCHAR)uni;
+ p = 0;
+ if (cp < 900) { /* SBCS */
+ for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get conversion table */
+ p = cp_table[i];
+ if (p) { /* Is it valid code page ? */
+ for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */
+ c = (c + 0x80) & 0xFF;
+ }
+ } else { /* DBCS */
+ switch (cp) { /* Get conversion table */
+ case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
+ case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
+ case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
+ case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
+ }
+ if (p) { /* Is it valid code page? */
+ li = 0;
+ for (n = 16; n; n--) { /* Find OEM code */
+ i = li + (hi - li) / 2;
+ if (uc == p[i * 2]) break;
+ if (uc > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+ }
+ }
+
+ return c;
+}
+
+
+WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
+ WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */
+ WORD cp /* Code page for the conversion */
+)
+{
+ const WCHAR *p;
+ WCHAR c = 0;
+ UINT i, n, li, hi;
+
+
+ if (oem < 0x80) { /* ASCII? */
+ c = oem;
+
+ } else { /* Extended char */
+ p = 0;
+ if (cp < 900) { /* SBCS */
+ for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */
+ p = cp_table[i];
+ if (p) { /* Is it a valid CP ? */
+ if (oem < 0x100) c = p[oem - 0x80];
+ }
+ } else { /* DBCS */
+ switch (cp) {
+ case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break;
+ case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break;
+ case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break;
+ case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break;
+ }
+ if (p) {
+ li = 0;
+ for (n = 16; n; n--) {
+ i = li + (hi - li) / 2;
+ if (oem == p[i * 2]) break;
+ if (oem > p[i * 2]) {
+ li = i;
+ } else {
+ hi = i;
+ }
+ }
+ if (n != 0) c = p[i * 2 + 1];
+ }
+ }
+ }
+
+ return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* Unicode up-case conversion */
+/*------------------------------------------------------------------------*/
+
+DWORD ff_wtoupper ( /* Returns up-converted code point */
+ DWORD uni /* Unicode code point to be up-converted */
+)
+{
+ const WORD *p;
+ WORD uc, bc, nc, cmd;
+ static const WORD cvt1[] = { /* Compressed up conversion table for U+0000 - U+0FFF */
+ /* Basic Latin */
+ 0x0061,0x031A,
+ /* Latin-1 Supplement */
+ 0x00E0,0x0317,
+ 0x00F8,0x0307,
+ 0x00FF,0x0001,0x0178,
+ /* Latin Extended-A */
+ 0x0100,0x0130,
+ 0x0132,0x0106,
+ 0x0139,0x0110,
+ 0x014A,0x012E,
+ 0x0179,0x0106,
+ /* Latin Extended-B */
+ 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
+ 0x01CD,0x0110,
+ 0x01DD,0x0001,0x018E,
+ 0x01DE,0x0112,
+ 0x01F3,0x0003,0x01F1,0x01F4,0x01F4,
+ 0x01F8,0x0128,
+ 0x0222,0x0112,
+ 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,
+ 0x0246,0x010A,
+ /* IPA Extensions */
+ 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
+ /* Greek, Coptic */
+ 0x037B,0x0003,0x03FD,0x03FE,0x03FF,
+ 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,
+ 0x03B1,0x0311,
+ 0x03C2,0x0002,0x03A3,0x03A3,
+ 0x03C4,0x0308,
+ 0x03CC,0x0003,0x038C,0x038E,0x038F,
+ 0x03D8,0x0118,
+ 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
+ /* Cyrillic */
+ 0x0430,0x0320,
+ 0x0450,0x0710,
+ 0x0460,0x0122,
+ 0x048A,0x0136,
+ 0x04C1,0x010E,
+ 0x04CF,0x0001,0x04C0,
+ 0x04D0,0x0144,
+ /* Armenian */
+ 0x0561,0x0426,
+
+ 0x0000 /* EOT */
+ };
+ static const WORD cvt2[] = { /* Compressed up conversion table for U+1000 - U+FFFF */
+ /* Phonetic Extensions */
+ 0x1D7D,0x0001,0x2C63,
+ /* Latin Extended Additional */
+ 0x1E00,0x0196,
+ 0x1EA0,0x015A,
+ /* Greek Extended */
+ 0x1F00,0x0608,
+ 0x1F10,0x0606,
+ 0x1F20,0x0608,
+ 0x1F30,0x0608,
+ 0x1F40,0x0606,
+ 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,
+ 0x1F60,0x0608,
+ 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
+ 0x1F80,0x0608,
+ 0x1F90,0x0608,
+ 0x1FA0,0x0608,
+ 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
+ 0x1FCC,0x0001,0x1FC3,
+ 0x1FD0,0x0602,
+ 0x1FE0,0x0602,
+ 0x1FE5,0x0001,0x1FEC,
+ 0x1FF3,0x0001,0x1FFC,
+ /* Letterlike Symbols */
+ 0x214E,0x0001,0x2132,
+ /* Number forms */
+ 0x2170,0x0210,
+ 0x2184,0x0001,0x2183,
+ /* Enclosed Alphanumerics */
+ 0x24D0,0x051A,
+ 0x2C30,0x042F,
+ /* Latin Extended-C */
+ 0x2C60,0x0102,
+ 0x2C67,0x0106, 0x2C75,0x0102,
+ /* Coptic */
+ 0x2C80,0x0164,
+ /* Georgian Supplement */
+ 0x2D00,0x0826,
+ /* Full-width */
+ 0xFF41,0x031A,
+
+ 0x0000 /* EOT */
+ };
+
+
+ if (uni < 0x10000) { /* Is it in BMP? */
+ uc = (WORD)uni;
+ p = uc < 0x1000 ? cvt1 : cvt2;
+ for (;;) {
+ bc = *p++; /* Get the block base */
+ if (bc == 0 || uc < bc) break; /* Not matched? */
+ nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
+ if (uc < bc + nc) { /* In the block? */
+ switch (cmd) {
+ case 0: uc = p[uc - bc]; break; /* Table conversion */
+ case 1: uc -= (uc - bc) & 1; break; /* Case pairs */
+ case 2: uc -= 16; break; /* Shift -16 */
+ case 3: uc -= 32; break; /* Shift -32 */
+ case 4: uc -= 48; break; /* Shift -48 */
+ case 5: uc -= 26; break; /* Shift -26 */
+ case 6: uc += 8; break; /* Shift +8 */
+ case 7: uc -= 80; break; /* Shift -80 */
+ case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */
+ }
+ break;
+ }
+ if (cmd == 0) p += nc; /* Skip table if needed */
+ }
+ uni = uc;
+ }
+
+ return uni;
+}
+
+#endif /* #if FF_USE_LFN */
diff --git a/bdk/libs/lv_conf.h b/bdk/libs/lv_conf.h
new file mode 100644
index 00000000..22590f7b
--- /dev/null
+++ b/bdk/libs/lv_conf.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2018 CTCaer
+ * Copyright (c) 2020 Storm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef LV_CONF_H
+#define LV_CONF_H
+
+#include
+#include
+/*===================
+ Dynamic memory
+ *===================*/
+
+/* Memory size which will be used by the library
+ * to store the graphical objects and other data */
+#define LV_MEM_CUSTOM 0 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/
+#if LV_MEM_CUSTOM == 0
+# define LV_MEM_SIZE NYX_LV_MEM_SZ /*Size memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
+# define LV_MEM_ATTR /*Complier prefix for big array declaration*/
+# define LV_MEM_ADR NYX_LV_MEM_ADR /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/
+# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/
+#else /*LV_MEM_CUSTOM*/
+# define LV_MEM_CUSTOM_INCLUDE "../../../mem/heap.h" /*Header for the dynamic memory function*/
+# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
+# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
+#endif /*LV_MEM_CUSTOM*/
+
+/* Garbage Collector settings
+ * Used if lvgl is binded to higher language and the memory is managed by that language */
+#define LV_ENABLE_GC 0
+#if LV_ENABLE_GC != 0
+# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
+# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
+# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
+#endif /* LV_ENABLE_GC */
+
+/*===================
+ Graphical settings
+ *===================*/
+
+/* Horizontal and vertical resolution of the library.*/
+#define LV_HOR_RES (1280)
+#define LV_VER_RES (720)
+
+#define LV_HOR_RES_MAX (1280)
+#define LV_VER_RES_MAX (720)
+
+/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide
+ * (Not so important, you can adjust it to modify default sizes and spaces)*/
+#define LV_DPI 100
+
+/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
+#define LV_ANTIALIAS 1 /*1: Enable anti-aliasing*/
+
+/*Screen refresh period in milliseconds*/
+#define LV_REFR_PERIOD 33
+
+/*-----------------
+ * VDB settings
+ *----------------*/
+
+/* VDB (Virtual Display Buffer) is an internal graphics buffer.
+ * The GUI will be drawn into this buffer first and then
+ * the buffer will be passed to your `disp_drv.disp_flush` function to
+ * copy it to your frame buffer.
+ * VDB is required for: buffered drawing, opacity, anti-aliasing and shadows
+ * Learn more: https://docs.littlevgl.com/#Drawing*/
+
+/* Size of the VDB in pixels. Typical size: ~1/10 screen. Must be >= LV_HOR_RES
+ * Setting it to 0 will disable VDB and `disp_drv.disp_fill` and `disp_drv.disp_map` functions
+ * will be called to draw to the frame buffer directly*/
+#define LV_VDB_SIZE (LV_VER_RES * LV_HOR_RES)
+
+ /* Bit-per-pixel of VDB. Useful for monochrome or non-standard color format displays.
+ * Special formats are handled with `disp_drv.vdb_wr`)*/
+#define LV_VDB_PX_BPP LV_COLOR_SIZE /*LV_COLOR_SIZE comes from LV_COLOR_DEPTH below to set 8, 16 or 32 bit pixel size automatically */
+
+ /* Place VDB to a specific address (e.g. in external RAM)
+ * 0: allocate automatically into RAM
+ * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
+#define LV_VDB_ADR NYX_LV_VDB_ADR
+
+/* Use two Virtual Display buffers (VDB) to parallelize rendering and flushing
+ * The flushing should use DMA to write the frame buffer in the background */
+#define LV_VDB_DOUBLE 0
+
+/* Place VDB2 to a specific address (e.g. in external RAM)
+ * 0: allocate automatically into RAM
+ * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/
+#define LV_VDB2_ADR 0
+
+/* Using true double buffering in `disp_drv.disp_flush` you will always get the image of the whole screen.
+ * Your only task is to set the rendered image (`color_p` parameter) as frame buffer address or send it to your display.
+ * The best if you do in the blank period of you display to avoid tearing effect.
+ * Requires:
+ * - LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES
+ * - LV_VDB_DOUBLE = 1
+ */
+#define LV_VDB_TRUE_DOUBLE_BUFFERED 0
+
+/*=================
+ Misc. setting
+ *=================*/
+
+/*Input device settings*/
+#define LV_INDEV_READ_PERIOD 33 /*Input device read period in milliseconds*/
+#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: USE_LV_REAL_DRAW = 1)*/
+#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */
+#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */
+#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/
+#define LV_INDEV_LONG_PRESS_REP_TIME 1000 //Fix keyb /*Repeated trigger period in long press [ms] */
+
+/*Color settings*/
+#define LV_COLOR_DEPTH 32 /*Color depth: 1/8/16/32*/
+#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
+#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/
+#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/
+
+/*Text settings*/
+#define LV_TXT_UTF8 0 /*Enable UTF-8 coded Unicode character usage */
+#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/
+#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
+#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
+#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
+
+/*Feature usage*/
+#define USE_LV_ANIMATION 1 /*1: Enable all animations*/
+#define USE_LV_SHADOW 1 /*1: Enable shadows*/
+#define USE_LV_GROUP 0 /*1: Enable object groups (for keyboards)*/
+#define USE_LV_GPU 0 /*1: Enable GPU interface*/
+#define USE_LV_REAL_DRAW 0 /*1: Enable function which draw directly to the frame buffer instead of VDB (required if LV_VDB_SIZE = 0)*/
+#define USE_LV_FILESYSTEM 0 /*1: Enable file system (might be required for images*/
+#define USE_LV_MULTI_LANG 0 /* Number of languages for labels to store (0: to disable this feature)*/
+
+/*Compiler settings*/
+#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */
+#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */
+#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/
+
+/*HAL settings*/
+#define LV_TICK_CUSTOM 1 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */
+#if LV_TICK_CUSTOM == 1
+#define LV_TICK_CUSTOM_INCLUDE "../../../utils/util.h" /*Header for the sys time function*/
+#define LV_TICK_CUSTOM_SYS_TIME_EXPR (get_tmr_ms()) /*Expression evaluating to current systime in ms*/
+#endif /*LV_TICK_CUSTOM*/
+
+
+/*Log settings*/
+#define USE_LV_LOG 0 /*Enable/disable the log module*/
+#if USE_LV_LOG
+/* How important log should be added:
+ * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
+ * LV_LOG_LEVEL_INFO Log important events
+ * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't caused problem
+ * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
+ */
+# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
+/* 1: Print the log with 'printf'; 0: user need to register a callback*/
+# define LV_LOG_PRINTF 1
+#endif /*USE_LV_LOG*/
+
+
+/*================
+ * THEME USAGE
+ *================*/
+#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
+
+#define USE_LV_THEME_STORM 1 //Theme initialisieren
+
+
+/*==================
+ * FONT USAGE
+ *===================*/
+
+/* More info about fonts: https://docs.littlevgl.com/#Fonts
+ * To enable a built-in font use 1,2,4 or 8 values
+ * which will determine the bit-per-pixel. Higher value means smoother fonts */
+#define LV_FONT_QUALITY 8
+
+#define USE_UBUNTU_MONO LV_FONT_QUALITY
+
+#define USE_INTERUI_20 LV_FONT_QUALITY
+#define USE_INTERUI_30 LV_FONT_QUALITY
+
+#define USE_HEKATE_SYMBOL_20 USE_INTERUI_20
+#define USE_HEKATE_SYMBOL_30 USE_INTERUI_30
+#define USE_HEKATE_SYMBOL_120 LV_FONT_QUALITY
+
+
+#define USE_MABOLT_12 2
+#define USE_NUM_110 2
+
+/* Optionally declare your custom fonts here.
+ * You can use these fonts as default font too
+ * and they will be available globally. E.g.
+ * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
+ * LV_FONT_DECLARE(my_font_2) \
+ */
+#define LV_FONT_CUSTOM_DECLARE
+
+#define LV_FONT_DEFAULT &interui_30 /*Always set a default font from the built-in fonts*/
+
+/*===================
+ * LV_OBJ SETTINGS
+ *==================*/
+#define LV_OBJ_FREE_NUM_TYPE uint32_t /*Type of free number attribute (comment out disable free number)*/
+#define LV_OBJ_FREE_PTR 1 /*Enable the free pointer attribute*/
+#define LV_OBJ_REALIGN 1 // 0 in OG gui /*Enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/
+
+/*==================
+ * LV OBJ X USAGE
+ *================*/
+/*
+ * Documentation of the object types: https://docs.littlevgl.com/#Object-types
+ */
+
+/*****************
+ * Simple object
+ *****************/
+
+/*Label (dependencies: -*/
+#define USE_LV_LABEL 1
+#if USE_LV_LABEL != 0
+# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
+#endif
+
+/*Image (dependencies: lv_label*/
+#define USE_LV_IMG 1
+#if USE_LV_IMG != 0
+# define LV_IMG_CF_INDEXED 0 /*Enable indexed (palette) images*/
+# define LV_IMG_CF_ALPHA 0 /*Enable alpha indexed images*/
+#endif
+
+/*Line (dependencies: -*/
+#define USE_LV_LINE 1
+
+/*Arc (dependencies: -)*/
+#define USE_LV_ARC 0
+
+/*******************
+ * Container objects
+ *******************/
+
+/*Container (dependencies: -*/
+#define USE_LV_CONT 1
+
+/*Page (dependencies: lv_cont)*/
+#define USE_LV_PAGE 1
+
+/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
+#define USE_LV_WIN 1
+
+/*Tab (dependencies: lv_page, lv_btnm)*/
+#define USE_LV_TABVIEW 1
+# if USE_LV_TABVIEW != 0
+# define LV_TABVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
+#endif
+
+/*Tileview (dependencies: lv_page) */
+#define USE_LV_TILEVIEW 0
+#if USE_LV_TILEVIEW
+# define LV_TILEVIEW_ANIM_TIME 0 /*Time of slide animation [ms] (0: no animation)*/
+#endif
+
+/*************************
+ * Data visualizer objects
+ *************************/
+
+/*Bar (dependencies: -)*/
+#define USE_LV_BAR 1
+
+/*Line meter (dependencies: *;)*/
+#define USE_LV_LMETER 0
+
+/*Gauge (dependencies:lv_bar, lv_lmeter)*/
+#define USE_LV_GAUGE 0
+
+/*Chart (dependencies: -)*/
+#define USE_LV_CHART 0
+
+/*Table (dependencies: lv_label)*/
+#define USE_LV_TABLE 1
+#if USE_LV_TABLE
+# define LV_TABLE_COL_MAX 12
+#endif
+
+/*LED (dependencies: -)*/
+#define USE_LV_LED 0
+
+/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
+#define USE_LV_MBOX 1
+
+/*Text area (dependencies: lv_label, lv_page)*/
+#define USE_LV_TA 1
+#if USE_LV_TA != 0
+# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/
+# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/
+#endif
+
+/*Spinbox (dependencies: lv_ta)*/
+#define USE_LV_SPINBOX 0
+
+/*Calendar (dependencies: -)*/
+#define USE_LV_CALENDAR 0
+
+/*Preload (dependencies: lv_arc)*/
+#define USE_LV_PRELOAD 0
+#if USE_LV_PRELOAD != 0
+# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
+# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
+# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
+#endif
+
+/*Canvas (dependencies: lv_img)*/
+#define USE_LV_CANVAS 0
+/*************************
+ * User input objects
+ *************************/
+
+/*Button (dependencies: lv_cont*/
+#define USE_LV_BTN 1
+#if USE_LV_BTN != 0
+# define LV_BTN_INK_EFFECT 0 /*Enable button-state animations - draw a circle on click (dependencies: USE_LV_ANIMATION)*/
+#endif
+
+/*Image Button (dependencies: lv_btn*/
+#define USE_LV_IMGBTN 1
+#if USE_LV_IMGBTN
+# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
+#endif
+
+/*Button matrix (dependencies: -)*/
+#define USE_LV_BTNM 1
+
+/*Keyboard (dependencies: lv_btnm)*/
+#define USE_LV_KB 1
+
+/*Check box (dependencies: lv_btn, lv_label)*/
+#define USE_LV_CB 1
+
+/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
+#define USE_LV_LIST 1
+#if USE_LV_LIST != 0
+# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */
+#endif
+
+/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
+#define USE_LV_DDLIST 1
+#if USE_LV_DDLIST != 0
+# define LV_DDLIST_ANIM_TIME 100 /*Open and close default animation time [ms] (0: no animation)*/
+#endif
+
+/*Roller (dependencies: lv_ddlist)*/
+#define USE_LV_ROLLER 1
+#if USE_LV_ROLLER != 0
+# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/
+#endif
+
+/*Slider (dependencies: lv_bar)*/
+#define USE_LV_SLIDER 1
+
+/*Switch (dependencies: lv_slider)*/
+#define USE_LV_SW 1
+
+
+
+
+
+
+#endif /*LV_CONF_H*/
+
diff --git a/bdk/thermal/fan.c b/bdk/thermal/fan.c
new file mode 100644
index 00000000..d149b360
--- /dev/null
+++ b/bdk/thermal/fan.c
@@ -0,0 +1,106 @@
+/*
+ * Fan driver for Nintendo Switch
+ *
+ * Copyright (c) 2018-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+void set_fan_duty(u32 duty)
+{
+ static bool fan_init = false;
+ static u16 curr_duty = -1;
+
+ if (curr_duty == duty)
+ return;
+
+ if (!fan_init)
+ {
+ // Fan tachometer.
+ PINMUX_AUX(PINMUX_AUX_CAM1_PWDN) = PINMUX_TRISTATE | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 1;
+ gpio_config(GPIO_PORT_S, GPIO_PIN_7, GPIO_MODE_GPIO);
+ gpio_output_enable(GPIO_PORT_S, GPIO_PIN_7, GPIO_OUTPUT_DISABLE);
+
+ PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (0x100 << 16); // Max PWM to disable fan.
+
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = 1; // Set source to PWM1.
+ gpio_config(GPIO_PORT_V, GPIO_PIN_4, GPIO_MODE_SPIO); // Fan power mode.
+
+ fan_init = true;
+ }
+
+ if (duty > 236)
+ duty = 236;
+
+ // Inverted polarity.
+ u32 inv_duty = 236 - duty;
+
+ // If disabled send a 0 duty.
+ if (inv_duty == 236)
+ {
+ PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (0x100 << 16); // Bit 24 is absolute 0%.
+ regulator_5v_disable(REGULATOR_5V_FAN);
+
+ // Disable fan.
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) =
+ PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_TRISTATE | PINMUX_PULL_DOWN; // Set source to PWM1.
+ }
+ else // Set PWM duty.
+ {
+ // Fan power supply.
+ regulator_5v_enable(REGULATOR_5V_FAN);
+ PWM(PWM_CONTROLLER_PWM_CSR_1) = PWM_CSR_EN | (inv_duty << 16);
+
+ // Enable fan.
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO2) = 1; // Set source to PWM1.
+ }
+
+ curr_duty = duty;
+}
+
+void get_fan_speed(u32 *duty, u32 *rpm)
+{
+ if (rpm)
+ {
+ u32 irq_count = 1;
+ bool should_read = true;
+ bool irq_val = 0;
+
+ // Poll irqs for 2 seconds.
+ int timer = get_tmr_us() + 1000000;
+ while (timer - get_tmr_us())
+ {
+ irq_val = gpio_read(GPIO_PORT_S, GPIO_PIN_7);
+ if (irq_val && should_read)
+ {
+ irq_count++;
+ should_read = false;
+ }
+ else if (!irq_val)
+ should_read = true;
+ }
+
+ // Calculate rpm based on triggered interrupts.
+ *rpm = 60000000 / ((1000000 * 2) / irq_count);
+ }
+
+ if (duty)
+ *duty = 236 - ((PWM(PWM_CONTROLLER_PWM_CSR_1) >> 16) & 0xFF);
+}
diff --git a/bdk/thermal/fan.h b/bdk/thermal/fan.h
new file mode 100644
index 00000000..e8279758
--- /dev/null
+++ b/bdk/thermal/fan.h
@@ -0,0 +1,29 @@
+/*
+ * Fan driver for Nintendo Switch
+ *
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __FAN_H_
+#define __FAN_H_
+
+#include
+
+// Disable: 0 (0 RPM), min duty: 1 (960 RPM), max duty 235 (11000 RPM).
+void set_fan_duty(u32 duty);
+// Passing NULL ptr on either of the two, disables parsing of it.
+void get_fan_speed(u32 *duty, u32 *rpm);
+
+#endif /* __FAN_H_ */
diff --git a/bdk/thermal/tmp451.c b/bdk/thermal/tmp451.c
new file mode 100644
index 00000000..65f2fd24
--- /dev/null
+++ b/bdk/thermal/tmp451.c
@@ -0,0 +1,87 @@
+/*
+ * SOC/PCB Temperature driver for Nintendo Switch's TI TMP451
+ *
+ * Copyright (c) 2018-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+u16 tmp451_get_soc_temp(bool intenger)
+{
+ u8 val;
+ u16 temp = 0;
+
+ val = i2c_recv_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TEMP_REG);
+ if (intenger)
+ return val;
+
+ temp = val << 8;
+ val = i2c_recv_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TMP_DEC_REG);
+ temp |= ((val >> 4) * 625) / 100;
+
+ return temp;
+}
+
+u16 tmp451_get_pcb_temp(bool intenger)
+{
+ u8 val;
+ u16 temp = 0;
+
+ val = i2c_recv_byte(I2C_1, TMP451_I2C_ADDR, TMP451_PCB_TEMP_REG);
+ if (intenger)
+ return val;
+
+ temp = val << 8;
+ val = i2c_recv_byte(I2C_1, TMP451_I2C_ADDR, TMP451_PCB_TMP_DEC_REG);
+ temp |= ((val >> 4) * 625) / 100;
+
+ return temp;
+}
+
+void tmp451_init()
+{
+ // Disable ALARM and Range to 0 - 127 oC.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_CONFIG_REG, 0x80);
+
+ // Set remote sensor offsets based on SoC.
+ if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210)
+ {
+ // Set offset to 0 oC for Erista.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TMP_OFH_REG, 0);
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TMP_OFL_REG, 0);
+ }
+ else
+ {
+ // Set offset to -12.5 oC for Mariko.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TMP_OFH_REG, 0xF3); // - 13 oC.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_SOC_TMP_OFL_REG, 0x80); // + 0.5 oC.
+ }
+
+ // Set conversion rate to 32/s and make a read to update the reg.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_CNV_RATE_REG, 9);
+ tmp451_get_soc_temp(false);
+
+ // Set rate to every 4 seconds.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_CNV_RATE_REG, 2);
+}
+
+void tmp451_end()
+{
+ // Place into shutdown mode to conserve power.
+ i2c_send_byte(I2C_1, TMP451_I2C_ADDR, TMP451_CONFIG_REG, 0xC0);
+}
diff --git a/bdk/thermal/tmp451.h b/bdk/thermal/tmp451.h
new file mode 100644
index 00000000..6258fbde
--- /dev/null
+++ b/bdk/thermal/tmp451.h
@@ -0,0 +1,46 @@
+/*
+ * SOC/PCB Temperature driver for Nintendo Switch's TI TMP451
+ *
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __TMP451_H_
+#define __TMP451_H_
+
+#include
+
+#define TMP451_I2C_ADDR 0x4C
+
+#define TMP451_PCB_TEMP_REG 0x00
+#define TMP451_SOC_TEMP_REG 0x01
+
+#define TMP451_CONFIG_REG 0x09
+#define TMP451_CNV_RATE_REG 0x0A
+
+#define TMP451_SOC_TMP_DEC_REG 0x10
+#define TMP451_PCB_TMP_DEC_REG 0x15
+
+#define TMP451_SOC_TMP_OFH_REG 0x11
+#define TMP451_SOC_TMP_OFL_REG 0x12
+
+// If input is false, the return value is packed. MSByte is the integer in oC
+// and the LSByte is the decimal point truncated to 2 decimal places.
+// Otherwise it's an integer oC.
+u16 tmp451_get_soc_temp(bool integer);
+u16 tmp451_get_pcb_temp(bool integer);
+void tmp451_init();
+void tmp451_end();
+
+#endif /* __TMP451_H_ */
diff --git a/bdk/usb/usb_descriptor_types.h b/bdk/usb/usb_descriptor_types.h
new file mode 100644
index 00000000..9f86e9d6
--- /dev/null
+++ b/bdk/usb/usb_descriptor_types.h
@@ -0,0 +1,238 @@
+/*
+ * USB driver for Tegra X1
+ *
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _USB_DESCRIPTORS_TYPES_H_
+#define _USB_DESCRIPTORS_TYPES_H_
+
+#include
+
+typedef enum {
+ USB_DESCRIPTOR_DEVICE = 1,
+ USB_DESCRIPTOR_CONFIGURATION = 2,
+ USB_DESCRIPTOR_STRING = 3,
+ USB_DESCRIPTOR_INTERFACE = 4,
+ USB_DESCRIPTOR_ENDPOINT = 5,
+ USB_DESCRIPTOR_DEVICE_QUALIFIER = 6,
+ USB_DESCRIPTOR_OTHER_SPEED_CONFIGURATION = 7,
+ USB_DESCRIPTOR_INTERFACE_POWER = 8,
+ USB_DESCRIPTOR_INTERFACE_OTG = 9,
+ USB_DESCRIPTOR_DEVICE_BINARY_OBJECT = 15,
+ USB_DESCRIPTOR_DEVICE_BINARY_OBJECT_CAP = 16,
+ USB_DESCRIPTOR_HID = 33,
+ USB_DESCRIPTOR_HID_REPORT = 34
+} usb_desc_type_t;
+
+typedef enum {
+ USB_DESCRIPTOR_MS_COMPAT_ID = 4,
+ USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES = 5
+} usb_vendor_desc_type_t;
+
+typedef enum {
+ USB_ATTR_REMOTE_WAKE_UP = 0x20,
+ USB_ATTR_SELF_POWERED = 0x40,
+ USB_ATTR_BUS_POWERED_RSVD = 0x80
+} usb_cfg_attr_type_t;
+
+typedef enum
+{
+ USB_EP_TYPE_CTRL = 0,
+ USB_EP_TYPE_ISO = 1,
+ USB_EP_TYPE_BULK = 2,
+ USB_EP_TYPE_INTR = 3
+} usb_cfg_ep_type_t;
+
+/* Device descriptor structure */
+typedef struct _usb_dev_descr_t
+{
+ u8 bLength; // Size of this descriptor in bytes.
+ u8 bDescriptorType; // Device Descriptor Type. (USB_DESCRIPTOR_DEVICE)
+ u16 bcdUSB; // USB Spec. Release number (2.1).
+ u8 bDeviceClass; // Class is specified in the interface descriptor.
+ u8 bDeviceSubClass; // SubClass is specified in the interface descriptor.
+ u8 bDeviceProtocol; // Protocol is specified in the interface descriptor.
+ u8 bMaxPacketSize; // Maximum packet size for EP0.
+ u16 idVendor; // Vendor ID assigned by USB forum.
+ u16 idProduct; // Product ID assigned by Organization.
+ u16 bcdDevice; // Device Release number in BCD.
+ u8 iManufacturer; // Index of String descriptor describing Manufacturer.
+ u8 iProduct; // Index of String descriptor describing Product.
+ u8 iSerialNumber; // Index of String descriptor describing Serial number.
+ u8 bNumConfigs; // Number of possible configuration.
+} __attribute__((packed)) usb_dev_descr_t;
+
+/* Device Qualifier descriptor structure */
+typedef struct _usb_dev_qual_descr_t
+{
+ u8 bLength; // Size of this descriptor in bytes.
+ u8 bDescriptorType; // Device Descriptor Type. (USB_DESCRIPTOR_DEVICE_QUALIFIER)
+ u16 bcdUSB; // USB Spec. Release number (2.1).
+ u8 bDeviceClass; // Class is specified in the interface descriptor.
+ u8 bDeviceSubClass; // SubClass is specified in the interface descriptor.
+ u8 bDeviceProtocol; // Protocol is specified in the interface descriptor.
+ u8 bMaxPacketSize; // Maximum packet size for EP0.
+ u8 bNumOtherConfigs; // Number of possible other-speed configurations.
+ u8 bReserved; // Reserved for future use, must be zero
+} __attribute__((packed)) usb_dev_qual_descr_t;
+
+/* Configuration descriptor structure */
+typedef struct _usb_cfg_descr_t
+{
+ u8 bLength; // Length of this descriptor.
+ u8 bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).
+ u16 wTotalLength; // Total length of all descriptors for this configuration.
+ u8 bNumInterfaces; // Number of interfaces in this configuration.
+ u8 bConfigurationValue; // Value of this configuration (1 based).
+ u8 iConfiguration; // Index of String Descriptor describing the configuration.
+ u8 bmAttributes; // Configuration characteristics.
+ u8 bMaxPower; // Maximum power consumed by this configuration.
+} __attribute__((packed)) usb_cfg_descr_t;
+
+/* Interface descriptor structure */
+typedef struct _usb_inter_descr_t
+{
+ u8 bLength; // Length of this descriptor.
+ u8 bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).
+ u8 bInterfaceNumber; // Number of this interface (0 based).
+ u8 bAlternateSetting; // Value of this alternate interface setting.
+ u8 bNumEndpoints; // Number of endpoints in this interface.
+ u8 bInterfaceClass; // Class code (assigned by the USB-IF).
+ u8 bInterfaceSubClass; // Subclass code (assigned by the USB-IF).
+ u8 bInterfaceProtocol; // Protocol code (assigned by the USB-IF).
+ u8 iInterface; // Index of String Descriptor describing the interface.
+} __attribute__((packed)) usb_inter_descr_t;
+
+/* HID descriptor structure */
+typedef struct _usb_hid_descr_t
+{
+ u8 bLength; // Length of this descriptor.
+ u8 bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_HID).
+ u16 bcdHID; // HID class specification release
+ u8 bCountryCode; // Country code.
+ u8 bNumDescriptors; // Number of descriptors.
+ u8 bClassDescriptorType; // Type of class descriptor (USB_DESCRIPTOR_HID_REPORT).
+ u16 bDescriptorLength; // Report descriptor length.
+} __attribute__((packed)) usb_hid_descr_t;
+
+/* Endpoint descriptor structure */
+typedef struct _usb_ep_descr_t
+{
+ u8 bLength; // Length of this descriptor.
+ u8 bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
+ u8 bEndpointAddress; // Endpoint address. bit7 indicates direction (0=OUT, 1=IN).
+ u8 bmAttributes; // Endpoint transfer type.
+ u16 wMaxPacketSize; // Maximum packet size.
+ u8 bInterval; // Polling interval in frames. For Interrupt and Isochronous data transfer only.
+} __attribute__((packed)) usb_ep_descr_t;
+
+typedef struct _usb_cfg_simple_descr_t
+{
+ usb_cfg_descr_t config;
+ usb_inter_descr_t interface;
+ usb_ep_descr_t endpoint[2];
+} __attribute__((packed)) usb_cfg_simple_descr_t;
+
+typedef struct _usb_cfg_hid_descr_t
+{
+ usb_cfg_descr_t config;
+ usb_inter_descr_t interface;
+ usb_hid_descr_t hid;
+ usb_ep_descr_t endpoint[2];
+} __attribute__((packed)) usb_cfg_hid_descr_t;
+
+typedef struct _usb_dev_bot_t
+{
+ u8 bLength; // Size of this descriptor in bytes.
+ u8 bDescriptorType; // Device Descriptor Type. (USB_DESCRIPTOR_DEVICE_BINARY_OBJECT)
+ u16 wTotalLength; // Size of this descriptor in bytes.
+ u8 bNumDeviceCaps; // Number of device capabilities in this descriptor.
+
+ /* Device Capability USB 2.0 Extension Descriptor */
+ /* Needed for a USB2.10 device. */
+ u8 bLengthCap0; // Size of this capability descriptor in bytes.
+ u8 bDescriptorTypeCap0; // Device Capability Descriptor Type. (USB_DESCRIPTOR_DEVICE_BINARY_OBJECT_CAP)
+ u8 bDevCapabilityTypeCap0; // USB2: 2.
+ u32 bmAttributesCap0; // bit1: Link Power Management (LPM).
+
+ u8 bLengthCap1; // Size of this capability descriptor in bytes.
+ u8 bDescriptorTypeCap1; // Device Capability Descriptor Type. (USB_DESCRIPTOR_DEVICE_BINARY_OBJECT_CAP)
+ u8 bDevCapabilityTypeCap1; // USB3: 3.
+ u8 bmAttributesCap1; // bit1: Latency Tolerance Messaging (LTM).
+ u16 wSpeedsSupported; // Supported bus speeds. 1: Low Speed, 2: Full Speed, 4: High Speed, 8: Super Speed.
+ u8 bFunctionalitySupport; // Lowest speed at which all the functionality is available. 1: Full speed and above.
+ u8 bU1DevExitLat; // USB3.0 U1 exit latency.
+ u16 wU2DevExitLat; // USB3.0 U2 exit latency.
+
+} __attribute__((packed)) usb_dev_bot_t;
+
+/* Microsoft OS String descriptor structure */
+typedef struct _usb_ms_os_descr_t
+{
+ u8 bLength; // 0x12
+ u8 bDescriptorType; // 3
+ u16 wSignature[7]; // "MSFT100" UTF16 LE
+ u8 bVendorCode; //
+ u8 bPadding;
+} __attribute__((packed)) usb_ms_os_descr_t;
+
+/* Microsoft Compatible ID Feature descriptor structure */
+typedef struct _usb_ms_cid_descr_t
+{
+ u32 dLength;
+ u16 wVersion;
+ u16 wCompatibilityId;
+ u8 bSections;
+ u8 bReserved0[7];
+ u8 bInterfaceNumber;
+ u8 bReserved1;
+ u8 bCompatibleId[8];
+ u8 bSubCompatibleId[8];
+ u8 bReserved2[6];
+} __attribute__((packed)) usb_ms_cid_descr_t;
+
+/* Microsoft Extended Properties Feature descriptor structure */
+typedef struct _usb_ms_ext_prop_descr_t
+{
+ u32 dLength;
+ u16 wVersion;
+ u16 wExtendedProperty;
+ u16 wSections;
+ u32 dPropertySize;
+ u32 dPropertyType;
+ u16 wPropertyNameLength;
+ u16 wPropertyName[22]; // UTF16 LE
+ u32 dPropertyDataLength;
+ u16 wPropertyData[2]; // UTF16 LE
+} __attribute__((packed)) usb_ms_ext_prop_descr_t;
+
+typedef struct _usb_desc_t
+{
+ usb_dev_descr_t *dev;
+ usb_dev_qual_descr_t *dev_qual;
+ usb_cfg_simple_descr_t *cfg;
+ usb_cfg_simple_descr_t *cfg_other;
+ usb_dev_bot_t *dev_bot;
+ u8 *vendor;
+ u8 *product;
+ u8 *serial;
+ u8 *lang_id;
+ usb_ms_os_descr_t *ms_os;
+ usb_ms_cid_descr_t *ms_cid;
+ usb_ms_ext_prop_descr_t *mx_ext;
+} usb_desc_t;
+
+#endif
diff --git a/bdk/usb/usb_descriptors.c b/bdk/usb/usb_descriptors.c
new file mode 100644
index 00000000..389a70d5
--- /dev/null
+++ b/bdk/usb/usb_descriptors.c
@@ -0,0 +1,558 @@
+/*
+ * USB driver for Tegra X1
+ *
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+static usb_dev_descr_t usb_device_descriptor_ums =
+{
+ .bLength = 18,
+ .bDescriptorType = USB_DESCRIPTOR_DEVICE,
+ .bcdUSB = 0x210,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize = 0x40,
+ .idVendor = 0x11EC, // Nintendo: 0x057E, Nvidia: 0x0955
+ .idProduct = 0xA7E0, // Switch: 0x2000, usbd: 0x3000
+ .bcdDevice = 0x0101,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigs = 1
+};
+
+static usb_dev_qual_descr_t usb_device_qualifier_descriptor =
+{
+ .bLength = 10,
+ .bDescriptorType = USB_DESCRIPTOR_DEVICE_QUALIFIER,
+ .bcdUSB = 0x210,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize = 0x40,
+ .bNumOtherConfigs = 0x01,
+ .bReserved = 0x00
+};
+
+static usb_cfg_simple_descr_t usb_configuration_descriptor_ums =
+{
+ /* Configuration descriptor structure */
+ .config.bLength = 9,
+ .config.bDescriptorType = USB_DESCRIPTOR_CONFIGURATION,
+ .config.wTotalLength = 0x20,
+ .config.bNumInterfaces = 0x01,
+ .config.bConfigurationValue = 0x01,
+ .config.iConfiguration = 0x00,
+ .config.bmAttributes = USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED_RSVD,
+ .config.bMaxPower = 32 / 2,
+
+ /* Interface descriptor structure */
+ .interface.bLength = 9,
+ .interface.bDescriptorType = USB_DESCRIPTOR_INTERFACE,
+ .interface.bInterfaceNumber = 0,
+ .interface.bAlternateSetting = 0,
+ .interface.bNumEndpoints = 2,
+ .interface.bInterfaceClass = 0x08, // Mass Storage Class.
+ .interface.bInterfaceSubClass = 0x06, // SCSI Transparent Command Set.
+ .interface.bInterfaceProtocol = 0x50, // Bulk-Only Transport.
+ .interface.iInterface = 0x00,
+
+ /* Endpoint descriptor structure EP1 IN */
+ .endpoint[0].bLength = 7,
+ .endpoint[0].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[0].bEndpointAddress = 0x81, // USB_EP_ADDR_BULK_IN.
+ .endpoint[0].bmAttributes = USB_EP_TYPE_BULK,
+ .endpoint[0].wMaxPacketSize = 0x200,
+ .endpoint[0].bInterval = 0x00,
+
+ /* Endpoint descriptor structure EP1 OUT */
+ .endpoint[1].bLength = 7,
+ .endpoint[1].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[1].bEndpointAddress = 0x01, // USB_EP_ADDR_BULK_OUT.
+ .endpoint[1].bmAttributes = USB_EP_TYPE_BULK,
+ .endpoint[1].wMaxPacketSize = 0x200,
+ .endpoint[1].bInterval = 0x00
+};
+
+static usb_cfg_simple_descr_t usb_other_speed_config_descriptor_ums =
+{
+ /* Other Speed Configuration descriptor structure */
+ .config.bLength = 9,
+ .config.bDescriptorType = USB_DESCRIPTOR_OTHER_SPEED_CONFIGURATION,
+ .config.wTotalLength = 0x20,
+ .config.bNumInterfaces = 0x01,
+ .config.bConfigurationValue = 0x01,
+ .config.iConfiguration = 0x00,
+ .config.bmAttributes = USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED_RSVD,
+ .config.bMaxPower = 32 / 2,
+
+ /* Interface descriptor structure */
+ .interface.bLength = 9,
+ .interface.bDescriptorType = USB_DESCRIPTOR_INTERFACE,
+ .interface.bInterfaceNumber = 0x00,
+ .interface.bAlternateSetting = 0x00,
+ .interface.bNumEndpoints = 2,
+ .interface.bInterfaceClass = 0x08, // Mass Storage Class.
+ .interface.bInterfaceSubClass = 0x06, // SCSI Transparent Command Set.
+ .interface.bInterfaceProtocol = 0x50, // Bulk-Only Transport.
+ .interface.iInterface = 0x00,
+
+ /* Endpoint descriptor structure EP1 IN */
+ .endpoint[0].bLength = 7,
+ .endpoint[0].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[0].bEndpointAddress = 0x81, // USB_EP_ADDR_BULK_IN.
+ .endpoint[0].bmAttributes = USB_EP_TYPE_BULK,
+ .endpoint[0].wMaxPacketSize = 0x40,
+ .endpoint[0].bInterval = 0,
+
+ /* Endpoint descriptor structure EP1 OUT */
+ .endpoint[1].bLength = 7,
+ .endpoint[1].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[1].bEndpointAddress = 0x01, // USB_EP_ADDR_BULK_OUT.
+ .endpoint[1].bmAttributes = USB_EP_TYPE_BULK,
+ .endpoint[1].wMaxPacketSize = 0x40,
+ .endpoint[1].bInterval = 0
+};
+
+static usb_dev_bot_t usb_device_binary_object_descriptor =
+{
+ .bLength = 5,
+ .bDescriptorType = USB_DESCRIPTOR_DEVICE_BINARY_OBJECT,
+ .wTotalLength = 22,
+ .bNumDeviceCaps = 2,
+
+ /* Device Capability USB 2.0 Extension Descriptor */
+ .bLengthCap0 = 7,
+ .bDescriptorTypeCap0 = USB_DESCRIPTOR_DEVICE_BINARY_OBJECT_CAP,
+ .bDevCapabilityTypeCap0 = 2, // USB2.
+ .bmAttributesCap0 = 0,
+
+ /* Device Capability SuperSpeed Descriptor */
+ /* Needed for a USB2.10 device. */
+ .bLengthCap1 = 10,
+ .bDescriptorTypeCap1 = USB_DESCRIPTOR_DEVICE_BINARY_OBJECT_CAP,
+ .bDevCapabilityTypeCap1 = 3, // USB3.
+ .bmAttributesCap1 = 0,
+ .wSpeedsSupported = 0x6, // FS | HS.
+ .bFunctionalitySupport = 1, // FS and above.
+ .bU1DevExitLat = 0,
+ .wU2DevExitLat = 0
+};
+
+static u8 usb_lang_id_string_descriptor[4] =
+{
+ 4, 3,
+ 0x09, 0x04
+};
+
+static u8 usb_serial_string_descriptor[26] =
+{
+ 26, 0x03,
+ 'C', 0x00, '7', 0x00, 'C', 0x00, '0', 0x00,
+ '9', 0x00, '2', 0x00, '4', 0x00, '2', 0x00, 'F', 0x00, '7', 0x00, '0', 0x00, '3', 0x00
+};
+
+static u8 usb_vendor_string_descriptor_ums[32] =
+{
+ 26, 0x03,
+ 'N', 0, 'y', 0, 'x', 0, ' ', 0, 'U', 0, 'S', 0, 'B', 0, ' ', 0,
+ 'D', 0, 'i', 0, 's', 0, 'k', 0
+};
+
+static u8 usb_product_string_descriptor_ums[22] =
+{
+ 8, 0x03,
+ 'U', 0, 'M', 0, 'S', 0
+};
+
+static usb_ms_os_descr_t usb_ms_os_descriptor =
+{
+ .bLength = 0x28,
+ .bDescriptorType = 0x03,
+ .wSignature[0] = 'M',
+ .wSignature[1] = 'S',
+ .wSignature[2] = 'F',
+ .wSignature[3] = 'T',
+ .wSignature[4] = '1',
+ .wSignature[5] = '0',
+ .wSignature[6] = '0',
+ .bVendorCode = 0x99,
+};
+
+static usb_ms_cid_descr_t usb_ms_cid_descriptor =
+{
+ .dLength = 0x28,
+ .wVersion = 0x100,
+ .wCompatibilityId = USB_DESCRIPTOR_MS_COMPAT_ID,
+ .bSections = 1,
+ .bInterfaceNumber = 0,
+ .bReserved1 = 1,
+
+ .bCompatibleId[0] = 'N',
+ .bCompatibleId[1] = 'Y',
+ .bCompatibleId[2] = 'X',
+ .bCompatibleId[3] = 'U',
+ .bCompatibleId[4] = 'S',
+ .bCompatibleId[5] = 'B',
+};
+
+static usb_ms_ext_prop_descr_t usb_ms_ext_prop_descriptor_ums =
+{
+ .dLength = 0x48,
+ .wVersion = 0x100,
+ .wExtendedProperty = USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES,
+ .wSections = 1,
+
+ .dPropertySize = 0x3E,
+ .dPropertyType = 4, // DWORD
+
+ .wPropertyNameLength = 0x2C,
+ .wPropertyName[0] = 'M', // MaximumTransferLength.
+ .wPropertyName[1] = 'a',
+ .wPropertyName[2] = 'x',
+ .wPropertyName[3] = 'i',
+ .wPropertyName[4] = 'm',
+ .wPropertyName[5] = 'u',
+ .wPropertyName[6] = 'm',
+ .wPropertyName[7] = 'T',
+ .wPropertyName[8] = 'r',
+ .wPropertyName[9] = 'a',
+ .wPropertyName[10] = 'n',
+ .wPropertyName[11] = 's',
+ .wPropertyName[12] = 'f',
+ .wPropertyName[13] = 'e',
+ .wPropertyName[14] = 'r',
+ .wPropertyName[15] = 'L',
+ .wPropertyName[16] = 'e',
+ .wPropertyName[17] = 'n',
+ .wPropertyName[18] = 'g',
+ .wPropertyName[19] = 't',
+ .wPropertyName[20] = 'h',
+ .wPropertyName[21] = 0,
+
+ .dPropertyDataLength = 0x4,
+ .wPropertyData[0] = 0x00, // 1MB.
+ .wPropertyData[1] = 0x10,
+};
+
+static usb_ms_ext_prop_descr_t usb_ms_ext_prop_descriptor_hid =
+{
+ .dLength = 7,
+ .wVersion = 0x100,
+ .wExtendedProperty = USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES,
+ .wSections = 0,
+};
+
+static usb_dev_descr_t usb_device_descriptor_hid_jc =
+{
+ .bLength = 18,
+ .bDescriptorType = USB_DESCRIPTOR_DEVICE,
+ .bcdUSB = 0x210,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize = 0x40,
+ .idVendor = 0x11EC, // Nintendo: 0x057E, Nvidia: 0x0955
+ .idProduct = 0xA7E1, // Switch: 0x2000, usbd: 0x3000
+ .bcdDevice = 0x0101,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigs = 1
+};
+
+static usb_dev_descr_t usb_device_descriptor_hid_touch =
+{
+ .bLength = 18,
+ .bDescriptorType = USB_DESCRIPTOR_DEVICE,
+ .bcdUSB = 0x210,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize = 0x40,
+ .idVendor = 0x11EC, // Nintendo: 0x057E, Nvidia: 0x0955
+ .idProduct = 0xA7E2, // Switch: 0x2000, usbd: 0x3000
+ .bcdDevice = 0x0101,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigs = 1
+};
+
+u8 hid_report_descriptor_jc[] =
+{
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop),
+ 0x09, 0x04, // USAGE (Joystick),
+ 0xa1, 0x01, // COLLECTION (Application),
+ 0xa1, 0x02, // COLLECTION (Logical),
+ 0x75, 0x08, // REPORT_SIZE (8),
+ 0x95, 0x04, // REPORT_COUNT (4),
+ 0x15, 0x00, // LOGICAL_MINIMUM (0),
+ 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255),
+ 0x35, 0x00, // PHYSICAL_MINIMUM (0),
+ 0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255),
+ 0x09, 0x30, // USAGE (X_ID),
+ 0x09, 0x31, // USAGE (Y_ID),
+ 0x09, 0x32, // USAGE (Z_ID),
+ 0x09, 0x35, // USAGE (Rz_ID),
+ 0x81, 0x02, // INPUT (IOF_Variable),
+ 0x75, 0x04, // REPORT_SIZE (4),
+ 0x95, 0x01, // REPORT_COUNT (1),
+ 0x25, 0x07, // LOGICAL_MAXIMUM (7),
+ 0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM (315),
+ 0x65, 0x14, // UNIT (Eng_Rot_Angular_Pos),
+ 0x09, 0x39, // USAGE (Hat_Switch),
+ 0x81, 0x42, // INPUT (IOF_NullposVar),
+ 0x65, 0x00, // UNIT (Unit_None),
+ 0x75, 0x01, // REPORT_SIZE (1),
+ 0x95, 0x0c, // REPORT_COUNT (12),
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1),
+ 0x45, 0x01, // PHYSICAL_MAXIMUM (1),
+ 0x05, 0x09, // USAGE_PAGE (Button_ID),
+ 0x19, 0x01, // USAGE_MINIMUM (1),
+ 0x29, 0x0c, // USAGE_MAXIMUM (12),
+ 0x81, 0x02, // INPUT (IOF_Variable),
+ 0xc0, // END_COLLECTION(),
+ 0xc0 // END_COLLECTION(),
+};
+
+u32 hid_report_descriptor_jc_size = sizeof(hid_report_descriptor_jc);
+
+u8 hid_report_descriptor_touch[] =
+{
+ 0x05, 0x0d, // USAGE_PAGE (Digitizers)
+ 0x09, 0x05, // USAGE (Touch Pad)
+ 0xa1, 0x01, // COLLECTION (Application)
+ 0x85, 0x05, // REPORT_ID (Touch pad)
+ 0x09, 0x22, // USAGE (Finger)
+ 0xa1, 0x02, // COLLECTION (Logical)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1)
+ 0x09, 0x42, // USAGE (Tip switch)
+ 0x95, 0x01, // REPORT_COUNT (1)
+ 0x75, 0x01, // REPORT_SIZE (1)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ 0x15, 0x00, // LOGICAL_MINIMUM (1)
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1)
+ 0x75, 0x01, // REPORT_SIZE (1)
+ 0x95, 0x07, // REPORT_COUNT (7)
+ 0x09, 0x54, // USAGE (Contact Count)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ 0x95, 0x01, // REPORT_COUNT (1)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x0A, // LOGICAL_MAXIMUM (10)
+ 0x09, 0x51, // USAGE (Contact Identifier)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ // 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ // 0x26, 0xF8, 0x2A, // LOGICAL_MAXIMUM (11000)
+ // 0x95, 0x01, // REPORT_COUNT (1)
+ // 0x75, 0x08, // REPORT_SIZE (16)
+ // 0x09, 0x30, // USAGE (Pressure)
+ // 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ 0x05, 0x01, // USAGE_PAGE (Generic Desk..
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x26, 0xff, 0x04, // LOGICAL_MAXIMUM (1279)
+ 0x75, 0x10, // REPORT_SIZE (16)
+ 0x55, 0x0e, // UNIT_EXPONENT (-2)
+ 0x65, 0x13, // UNIT(Inch,EngLinear)
+ 0x09, 0x30, // USAGE (X)
+ 0x35, 0x00, // PHYSICAL_MINIMUM (0)
+ 0x46, 0xFF, 0x04, // PHYSICAL_MAXIMUM (1279)
+ 0x95, 0x01, // REPORT_COUNT (1)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0x26, 0xCF, 0x02, // LOGICAL_MAXIMUM (719)
+ 0x46, 0xCF, 0x02, // PHYSICAL_MAXIMUM (719)
+ 0x09, 0x31, // USAGE (Y)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ 0x05, 0x0d, // USAGE PAGE (Digitizers)
+ 0xc0, // END_COLLECTION
+ 0xc0, // END_COLLECTION
+};
+u32 hid_report_descriptor_touch_size = sizeof(hid_report_descriptor_touch);
+
+static usb_cfg_hid_descr_t usb_configuration_descriptor_hid_jc =
+{
+ /* Configuration descriptor structure */
+ .config.bLength = 9,
+ .config.bDescriptorType = USB_DESCRIPTOR_CONFIGURATION,
+ .config.wTotalLength = sizeof(usb_cfg_hid_descr_t),
+ .config.bNumInterfaces = 0x01,
+ .config.bConfigurationValue = 0x01,
+ .config.iConfiguration = 0x00,
+ .config.bmAttributes = USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED_RSVD,
+ .config.bMaxPower = 32 / 2,
+
+ /* Interface descriptor structure */
+ .interface.bLength = 9,
+ .interface.bDescriptorType = USB_DESCRIPTOR_INTERFACE,
+ .interface.bInterfaceNumber = 0,
+ .interface.bAlternateSetting = 0,
+ .interface.bNumEndpoints = 2,
+ .interface.bInterfaceClass = 0x03, // Human Interface Device Class.
+ .interface.bInterfaceSubClass = 0x00, // SCSI Transparent Command Set.
+ .interface.bInterfaceProtocol = 0x00, // Bulk-Only Transport.
+ .interface.iInterface = 0x00,
+
+ .hid.bLength = 9,
+ .hid.bDescriptorType = USB_DESCRIPTOR_HID,
+ .hid.bcdHID = 0x110,
+ .hid.bCountryCode = 0,
+ .hid.bNumDescriptors = 1,
+ .hid.bClassDescriptorType = USB_DESCRIPTOR_HID_REPORT,
+ .hid.bDescriptorLength = sizeof(hid_report_descriptor_jc),
+
+ /* Endpoint descriptor structure EP1 IN */
+ .endpoint[0].bLength = 7,
+ .endpoint[0].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[0].bEndpointAddress = 0x81, // USB_EP_ADDR_BULK_IN.
+ .endpoint[0].bmAttributes = USB_EP_TYPE_INTR,
+ .endpoint[0].wMaxPacketSize = 0x200,
+ .endpoint[0].bInterval = 4, // 8ms on HS.
+
+ /* Endpoint descriptor structure EP1 OUT */
+ .endpoint[1].bLength = 7,
+ .endpoint[1].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[1].bEndpointAddress = 0x01, // USB_EP_ADDR_BULK_OUT.
+ .endpoint[1].bmAttributes = USB_EP_TYPE_INTR,
+ .endpoint[1].wMaxPacketSize = 0x200,
+ .endpoint[1].bInterval = 4 // 8ms on HS.
+};
+
+static u8 usb_vendor_string_descriptor_hid[22] =
+{
+ 16, 0x03,
+ 'N', 0, 'y', 0, 'x', 0, ' ', 0,
+ 'U', 0, 'S', 0, 'B', 0
+};
+
+static u8 usb_product_string_descriptor_hid_jc[24] =
+{
+ 24, 0x03,
+ 'N', 0, 'y', 0, 'x', 0, ' ', 0,
+ 'J', 0, 'o', 0, 'y', 0, '-', 0, 'C', 0, 'o', 0, 'n', 0
+};
+
+static u8 usb_product_string_descriptor_hid_touch[26] =
+{
+ 26, 0x03,
+ 'N', 0, 'y', 0, 'x', 0, ' ', 0,
+ 'T', 0, 'o', 0, 'u', 0, 'c', 0, 'h', 0, 'p', 0, 'a', 0, 'd', 0
+};
+
+static usb_cfg_hid_descr_t usb_configuration_descriptor_hid_touch =
+{
+ /* Configuration descriptor structure */
+ .config.bLength = 9,
+ .config.bDescriptorType = USB_DESCRIPTOR_CONFIGURATION,
+ .config.wTotalLength = sizeof(usb_cfg_hid_descr_t),
+ .config.bNumInterfaces = 0x01,
+ .config.bConfigurationValue = 0x01,
+ .config.iConfiguration = 0x00,
+ .config.bmAttributes = USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED_RSVD,
+ .config.bMaxPower = 32 / 2,
+
+ /* Interface descriptor structure */
+ .interface.bLength = 9,
+ .interface.bDescriptorType = USB_DESCRIPTOR_INTERFACE,
+ .interface.bInterfaceNumber = 0,
+ .interface.bAlternateSetting = 0,
+ .interface.bNumEndpoints = 2,
+ .interface.bInterfaceClass = 0x03, // Human Interface Device Class.
+ .interface.bInterfaceSubClass = 0x00, // SCSI Transparent Command Set.
+ .interface.bInterfaceProtocol = 0x00, // Bulk-Only Transport.
+ .interface.iInterface = 0x00,
+
+ .hid.bLength = 9,
+ .hid.bDescriptorType = USB_DESCRIPTOR_HID,
+ .hid.bcdHID = 0x111,
+ .hid.bCountryCode = 0,
+ .hid.bNumDescriptors = 1,
+ .hid.bClassDescriptorType = USB_DESCRIPTOR_HID_REPORT,
+ .hid.bDescriptorLength = sizeof(hid_report_descriptor_touch),
+
+ /* Endpoint descriptor structure EP1 IN */
+ .endpoint[0].bLength = 7,
+ .endpoint[0].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[0].bEndpointAddress = 0x81, // USB_EP_ADDR_BULK_IN.
+ .endpoint[0].bmAttributes = USB_EP_TYPE_INTR,
+ .endpoint[0].wMaxPacketSize = 0x200,
+ .endpoint[0].bInterval = 3, // 4ms on HS.
+
+ /* Endpoint descriptor structure EP1 OUT */
+ .endpoint[1].bLength = 7,
+ .endpoint[1].bDescriptorType = USB_DESCRIPTOR_ENDPOINT,
+ .endpoint[1].bEndpointAddress = 0x01, // USB_EP_ADDR_BULK_OUT.
+ .endpoint[1].bmAttributes = USB_EP_TYPE_INTR,
+ .endpoint[1].wMaxPacketSize = 0x200,
+ .endpoint[1].bInterval = 3 // 4ms on HS.
+};
+
+usb_desc_t usb_gadget_ums_descriptors =
+{
+ .dev = &usb_device_descriptor_ums,
+ .dev_qual = &usb_device_qualifier_descriptor,
+ .cfg = &usb_configuration_descriptor_ums,
+ .cfg_other = &usb_other_speed_config_descriptor_ums,
+ .dev_bot = &usb_device_binary_object_descriptor,
+ .vendor = usb_vendor_string_descriptor_ums,
+ .product = usb_product_string_descriptor_ums,
+ .serial = usb_serial_string_descriptor,
+ .lang_id = usb_lang_id_string_descriptor,
+ .ms_os = &usb_ms_os_descriptor,
+ .ms_cid = &usb_ms_cid_descriptor,
+ .mx_ext = &usb_ms_ext_prop_descriptor_ums
+};
+
+usb_desc_t usb_gadget_hid_jc_descriptors =
+{
+ .dev = &usb_device_descriptor_hid_jc,
+ .dev_qual = &usb_device_qualifier_descriptor,
+ .cfg = (usb_cfg_simple_descr_t *)&usb_configuration_descriptor_hid_jc,
+ .cfg_other = NULL,
+ .dev_bot = &usb_device_binary_object_descriptor,
+ .vendor = usb_vendor_string_descriptor_hid,
+ .product = usb_product_string_descriptor_hid_jc,
+ .serial = usb_serial_string_descriptor,
+ .lang_id = usb_lang_id_string_descriptor,
+ .ms_os = &usb_ms_os_descriptor,
+ .ms_cid = &usb_ms_cid_descriptor,
+ .mx_ext = &usb_ms_ext_prop_descriptor_hid
+};
+
+usb_desc_t usb_gadget_hid_touch_descriptors =
+{
+ .dev = &usb_device_descriptor_hid_touch,
+ .dev_qual = &usb_device_qualifier_descriptor,
+ .cfg = (usb_cfg_simple_descr_t *)&usb_configuration_descriptor_hid_touch,
+ .cfg_other = NULL,
+ .dev_bot = &usb_device_binary_object_descriptor,
+ .vendor = usb_vendor_string_descriptor_hid,
+ .product = usb_product_string_descriptor_hid_touch,
+ .serial = usb_serial_string_descriptor,
+ .lang_id = usb_lang_id_string_descriptor,
+ .ms_os = &usb_ms_os_descriptor,
+ .ms_cid = &usb_ms_cid_descriptor,
+ .mx_ext = &usb_ms_ext_prop_descriptor_hid
+};
diff --git a/bdk/usb/usb_gadget_hid.c b/bdk/usb/usb_gadget_hid.c
new file mode 100644
index 00000000..b7c2e24d
--- /dev/null
+++ b/bdk/usb/usb_gadget_hid.c
@@ -0,0 +1,441 @@
+/*
+ * USB Gadget HID driver for Tegra X1
+ *
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+//#define DPRINTF(...) gfx_printf(__VA_ARGS__)
+#define DPRINTF(...)
+
+typedef struct _gamepad_report_t
+{
+ u8 x;
+ u8 y;
+ u8 z;
+ u8 rz;
+
+ u8 hat:4;
+ u8 btn1:1;
+ u8 btn2:1;
+ u8 btn3:1;
+ u8 btn4:1;
+
+ u8 btn5:1;
+ u8 btn6:1;
+ u8 btn7:1;
+ u8 btn8:1;
+ u8 btn9:1;
+ u8 btn10:1;
+ u8 btn11:1;
+ u8 btn12:1;
+} __attribute__((packed)) gamepad_report_t;
+
+typedef struct _jc_cal_t
+{
+ bool cl_done;
+ bool cr_done;
+ u16 clx_max;
+ u16 clx_min;
+ u16 cly_max;
+ u16 cly_min;
+ u16 crx_max;
+ u16 crx_min;
+ u16 cry_max;
+ u16 cry_min;
+} jc_cal_t;
+
+static jc_cal_t jc_cal_ctx;
+static usb_ops_t usb_ops;
+
+static bool _jc_calibration(jc_gamepad_rpt_t *jc_pad)
+{
+ // Calibrate left stick.
+ if (!jc_cal_ctx.cl_done)
+ {
+ if (jc_pad->conn_l
+ && jc_pad->lstick_x > 0x400 && jc_pad->lstick_y > 0x400
+ && jc_pad->lstick_x < 0xC00 && jc_pad->lstick_y < 0xC00)
+ {
+ jc_cal_ctx.clx_max = jc_pad->lstick_x + 0x72;
+ jc_cal_ctx.clx_min = jc_pad->lstick_x - 0x72;
+ jc_cal_ctx.cly_max = jc_pad->lstick_y + 0x72;
+ jc_cal_ctx.cly_min = jc_pad->lstick_y - 0x72;
+ jc_cal_ctx.cl_done = true;
+ }
+ else
+ return false;
+ }
+
+ // Calibrate right stick.
+ if (!jc_cal_ctx.cr_done)
+ {
+ if (jc_pad->conn_r
+ && jc_pad->rstick_x > 0x400 && jc_pad->rstick_y > 0x400
+ && jc_pad->rstick_x < 0xC00 && jc_pad->rstick_y < 0xC00)
+ {
+ jc_cal_ctx.crx_max = jc_pad->rstick_x + 0x72;
+ jc_cal_ctx.crx_min = jc_pad->rstick_x - 0x72;
+ jc_cal_ctx.cry_max = jc_pad->rstick_y + 0x72;
+ jc_cal_ctx.cry_min = jc_pad->rstick_y - 0x72;
+ jc_cal_ctx.cr_done = true;
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static bool _jc_poll(gamepad_report_t *rpt)
+{
+ // Poll Joy-Con.
+ jc_gamepad_rpt_t *jc_pad = joycon_poll();
+
+ if (!jc_pad)
+ return false;
+
+ // Exit emulation if Left stick and Home are pressed.
+ if (jc_pad->l3 && jc_pad->home)
+ return true;
+
+ if (!jc_cal_ctx.cl_done || !jc_cal_ctx.cr_done)
+ {
+ if (!_jc_calibration(jc_pad))
+ return false;
+ }
+
+ // Re-calibrate on disconnection.
+ if (!jc_pad->conn_l)
+ jc_cal_ctx.cl_done = false;
+ if (!jc_pad->conn_r)
+ jc_cal_ctx.cr_done = false;
+
+ // Calculate left analog stick.
+ if (jc_pad->lstick_x <= jc_cal_ctx.clx_max && jc_pad->lstick_x >= jc_cal_ctx.clx_min)
+ rpt->x = 0x7F;
+ else if (jc_pad->lstick_x > jc_cal_ctx.clx_max)
+ {
+ u16 x_raw = (jc_pad->lstick_x - jc_cal_ctx.clx_max) / 7;
+ if (x_raw > 0x7F)
+ x_raw = 0x7F;
+ rpt->x = 0x7F + x_raw;
+ }
+ else
+ {
+ u16 x_raw = (jc_cal_ctx.clx_min - jc_pad->lstick_x) / 7;
+ if (x_raw > 0x7F)
+ x_raw = 0x7F;
+ rpt->x = 0x7F - x_raw;
+ }
+
+ if (jc_pad->lstick_y <= jc_cal_ctx.cly_max && jc_pad->lstick_y >= jc_cal_ctx.cly_min)
+ rpt->y = 0x7F;
+ else if (jc_pad->lstick_y > jc_cal_ctx.cly_max)
+ {
+ u16 y_raw = (jc_pad->lstick_y - jc_cal_ctx.cly_max) / 7;
+ if (y_raw > 0x7F)
+ y_raw = 0x7F;
+ rpt->y = 0x7F - y_raw;
+ }
+ else
+ {
+ u16 y_raw = (jc_cal_ctx.cly_min - jc_pad->lstick_y) / 7;
+ if (y_raw > 0x7F)
+ y_raw = 0x7F;
+ rpt->y = 0x7F + y_raw;
+ }
+
+ // Calculate right analog stick.
+ if (jc_pad->rstick_x <= jc_cal_ctx.crx_max && jc_pad->rstick_x >= jc_cal_ctx.crx_min)
+ rpt->z = 0x7F;
+ else if (jc_pad->rstick_x > jc_cal_ctx.crx_max)
+ {
+ u16 x_raw = (jc_pad->rstick_x - jc_cal_ctx.crx_max) / 7;
+ if (x_raw > 0x7F)
+ x_raw = 0x7F;
+ rpt->z = 0x7F + x_raw;
+ }
+ else
+ {
+ u16 x_raw = (jc_cal_ctx.crx_min - jc_pad->rstick_x) / 7;
+ if (x_raw > 0x7F)
+ x_raw = 0x7F;
+ rpt->z = 0x7F - x_raw;
+ }
+
+ if (jc_pad->rstick_y <= jc_cal_ctx.cry_max && jc_pad->rstick_y >= jc_cal_ctx.cry_min)
+ rpt->rz = 0x7F;
+ else if (jc_pad->rstick_y > jc_cal_ctx.cry_max)
+ {
+ u16 y_raw = (jc_pad->rstick_y - jc_cal_ctx.cry_max) / 7;
+ if (y_raw > 0x7F)
+ y_raw = 0x7F;
+ rpt->rz = 0x7F - y_raw;
+ }
+ else
+ {
+ u16 y_raw = (jc_cal_ctx.cry_min - jc_pad->rstick_y) / 7;
+ if (y_raw > 0x7F)
+ y_raw = 0x7F;
+ rpt->rz = 0x7F + y_raw;
+ }
+
+ // Set D-pad.
+ switch ((jc_pad->buttons >> 16) & 0xF)
+ {
+ case 0: // none
+ rpt->hat = 0xF;
+ break;
+ case 1: // down
+ rpt->hat = 4;
+ break;
+ case 2: // up
+ rpt->hat = 0;
+ break;
+ case 4: // right
+ rpt->hat = 2;
+ break;
+ case 5: // down + right
+ rpt->hat = 3;
+ break;
+ case 6: // up + right
+ rpt->hat = 1;
+ break;
+ case 8: // left
+ rpt->hat = 6;
+ break;
+ case 9: // down + left
+ rpt->hat = 5;
+ break;
+ case 10: // up + left
+ rpt->hat = 7;
+ break;
+ default:
+ rpt->hat = 0xF;
+ break;
+ }
+
+ // Set buttons.
+ rpt->btn1 = jc_pad->b; // x.
+ rpt->btn2 = jc_pad->a; // a.
+ rpt->btn3 = jc_pad->y; // b.
+ rpt->btn4 = jc_pad->x; // y.
+
+ rpt->btn5 = jc_pad->l;
+ rpt->btn6 = jc_pad->r;
+ rpt->btn7 = jc_pad->zl;
+ rpt->btn8 = jc_pad->zr;
+ rpt->btn9 = jc_pad->minus;
+ rpt->btn10 = jc_pad->plus;
+ rpt->btn11 = jc_pad->l3;
+ rpt->btn12 = jc_pad->r3;
+
+ //rpt->btn13 = jc_pad->cap;
+ //rpt->btn14 = jc_pad->home;
+
+ return false;
+}
+
+typedef struct _touchpad_report_t
+{
+ u8 rpt_id;
+ u8 tip_switch:1;
+ u8 count:7;
+
+ u8 id;
+
+ //u16 z;
+ u16 x;
+ u16 y;
+} __attribute__((packed)) touchpad_report_t;
+
+static bool _fts_touch_read(touchpad_report_t *rpt)
+{
+ static touch_event touchpad;
+
+ touch_poll(&touchpad);
+
+ rpt->rpt_id = 5;
+ rpt->count = 1;
+
+ // Decide touch enable.
+ switch (touchpad.type & STMFTS_MASK_EVENT_ID)
+ {
+ //case STMFTS_EV_MULTI_TOUCH_ENTER:
+ case STMFTS_EV_MULTI_TOUCH_MOTION:
+ rpt->x = touchpad.x;
+ rpt->y = touchpad.y;
+ //rpt->z = touchpad.z;
+ rpt->id = touchpad.fingers ? touchpad.fingers - 1 : 0;
+ rpt->tip_switch = 1;
+ break;
+ case STMFTS_EV_MULTI_TOUCH_LEAVE:
+ rpt->x = touchpad.x;
+ rpt->y = touchpad.y;
+ //rpt->z = touchpad.z;
+ rpt->id = touchpad.fingers ? touchpad.fingers - 1 : 0;
+ rpt->tip_switch = 0;
+ break;
+ case STMFTS_EV_NO_EVENT:
+ return false;
+ }
+
+ return true;
+}
+
+static u8 _hid_transfer_start(usb_ctxt_t *usbs, u32 len)
+{
+ u8 status = usb_ops.usb_device_ep1_in_write((u8 *)USB_EP_BULK_IN_BUF_ADDR, len, NULL, USB_XFER_SYNCED_CMD);
+ if (status == USB_ERROR_XFER_ERROR)
+ {
+ usbs->set_text(usbs->label, "#FFDD00 Error:# EP IN transfer!");
+ if (usb_ops.usbd_flush_endpoint)
+ usb_ops.usbd_flush_endpoint(USB_EP_BULK_IN);
+ }
+
+ // Linux mitigation: If timed out, clear status.
+ if (status == USB_ERROR_TIMEOUT)
+ return 0;
+
+ return status;
+}
+
+static bool _hid_poll_jc(usb_ctxt_t *usbs)
+{
+ if (_jc_poll((gamepad_report_t *)USB_EP_BULK_IN_BUF_ADDR))
+ return true;
+
+ // Send HID report.
+ if (_hid_transfer_start(usbs, sizeof(gamepad_report_t)))
+ return true; // EP Error.
+
+ return false;
+}
+
+static bool _hid_poll_touch(usb_ctxt_t *usbs)
+{
+ _fts_touch_read((touchpad_report_t *)USB_EP_BULK_IN_BUF_ADDR);
+
+ // Send HID report.
+ if (_hid_transfer_start(usbs, sizeof(touchpad_report_t)))
+ return true; // EP Error.
+
+ return false;
+}
+
+int usb_device_gadget_hid(usb_ctxt_t *usbs)
+{
+ int res = 0;
+ u32 gadget_type;
+ u32 polling_time;
+
+ // Get USB Controller ops.
+ if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210)
+ usb_device_get_ops(&usb_ops);
+ else
+ xusb_device_get_ops(&usb_ops);
+
+ if (usbs->type == USB_HID_GAMEPAD)
+ {
+ polling_time = 8000;
+ gadget_type = USB_GADGET_HID_GAMEPAD;
+ }
+ else
+ {
+ polling_time = 4000;
+ gadget_type = USB_GADGET_HID_TOUCHPAD;
+ }
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# Started USB");
+
+ if (usb_ops.usb_device_init())
+ {
+ usb_ops.usbd_end(false, true);
+ return 1;
+ }
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# Waiting for connection");
+
+ // Initialize Control Endpoint.
+ if (usb_ops.usb_device_enumerate(gadget_type))
+ goto error;
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# Waiting for HID report request");
+
+ if (usb_ops.usb_device_class_send_hid_report())
+ goto error;
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# Started HID emulation");
+
+ u32 timer_sys = get_tmr_ms() + 5000;
+ while (true)
+ {
+ u32 timer = get_tmr_us();
+
+ // Parse input device.
+ if (usbs->type == USB_HID_GAMEPAD)
+ {
+ if (_hid_poll_jc(usbs))
+ break;
+ }
+ else
+ {
+ if (_hid_poll_touch(usbs))
+ break;
+ }
+
+ // Check for suspended USB in case the cable was pulled.
+ if (usb_ops.usb_device_get_suspended())
+ break; // Disconnected.
+
+ // Handle control endpoint.
+ usb_ops.usbd_handle_ep0_ctrl_setup();
+
+ // Wait max gadget timing.
+ timer = get_tmr_us() - timer;
+ if (timer < polling_time)
+ usleep(polling_time - timer);
+
+ if (timer_sys < get_tmr_ms())
+ {
+ usbs->system_maintenance(true);
+ timer_sys = get_tmr_ms() + 5000;
+ }
+ }
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# HID ended");
+ goto exit;
+
+error:
+ usbs->set_text(usbs->label, "#FFDD00 Error:# Timed out or canceled");
+ res = 1;
+
+exit:
+ usb_ops.usbd_end(true, false);
+
+ return res;
+}
diff --git a/bdk/usb/usb_gadget_ums.c b/bdk/usb/usb_gadget_ums.c
new file mode 100644
index 00000000..380cfa98
--- /dev/null
+++ b/bdk/usb/usb_gadget_ums.c
@@ -0,0 +1,1925 @@
+/*
+ * USB Gadget UMS driver for Tegra X1
+ *
+ * Copyright (c) 2003-2008 Alan Stern
+ * Copyright (c) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz
+ * Copyright (c) 2019-2020 CTCaer
+ * Copyright (c) 2020 Storm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+//#define DPRINTF(...) gfx_printf(__VA_ARGS__)
+#define DPRINTF(...)
+
+#define UMS_MAX_LUN 1 // Only 1 disk/partition for now.
+
+#define USB_BULK_CB_WRAP_LEN 31
+#define USB_BULK_CB_SIG 0x43425355 // USBC.
+#define USB_BULK_IN_FLAG 0x80
+
+#define USB_BULK_CS_WRAP_LEN 13
+#define USB_BULK_CS_SIG 0x53425355 // USBS.
+
+#define USB_STATUS_PASS 0
+#define USB_STATUS_FAIL 1
+#define USB_STATUS_PHASE_ERROR 2
+
+#define UMS_DISK_LBA_SHIFT 9
+#define UMS_DISK_LBA_SIZE (1 << UMS_DISK_LBA_SHIFT)
+
+#define UMS_DISK_MAX_IO_TRANSFER_64K (USB_EP_BUFFER_MAX_SIZE >> UMS_DISK_LBA_SHIFT)
+#define UMS_DISK_MAX_IO_TRANSFER_32K (UMS_DISK_MAX_IO_TRANSFER_64K / 2)
+
+#define UMS_SCSI_TRANSFER_512K (0x80000 >> UMS_DISK_LBA_SHIFT)
+
+#define UMS_EP_OUT_MAX_XFER (USB_EP_BULK_OUT_MAX_XFER)
+
+// Length of a SCSI Command Data Block.
+#define SCSI_MAX_CMD_SZ 16
+
+// SCSI device types
+#define SCSI_TYPE_DISK 0x00
+
+// SCSI commands.
+#define SC_FORMAT_UNIT 0x04
+#define SC_INQUIRY 0x12
+#define SC_LOG_SENSE 0x4D
+#define SC_MODE_SELECT_6 0x15
+#define SC_MODE_SELECT_10 0x55
+#define SC_MODE_SENSE_6 0x1A
+#define SC_MODE_SENSE_10 0x5A
+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
+#define SC_READ_6 0x08
+#define SC_READ_10 0x28
+#define SC_READ_12 0xA8
+#define SC_READ_CAPACITY 0x25
+#define SC_READ_FORMAT_CAPACITIES 0x23
+#define SC_READ_HEADER 0x44
+#define SC_READ_TOC 0x43
+#define SC_RELEASE 0x17
+#define SC_REQUEST_SENSE 0x03
+#define SC_RESERVE 0x16
+#define SC_SEND_DIAGNOSTIC 0x1D
+#define SC_START_STOP_UNIT 0x1B
+#define SC_SYNCHRONIZE_CACHE 0x35
+#define SC_TEST_UNIT_READY 0x00
+#define SC_VERIFY 0x2F
+#define SC_WRITE_6 0x0A
+#define SC_WRITE_10 0x2A
+#define SC_WRITE_12 0xAA
+
+// SCSI Sense Key/Additional Sense Code/ASC Qualifier values.
+#define SS_NO_SENSE 0x0
+#define SS_COMMUNICATION_FAILURE 0x40800
+#define SS_INVALID_COMMAND 0x52000
+#define SS_INVALID_FIELD_IN_CDB 0x52400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x52100
+#define SS_MEDIUM_NOT_PRESENT 0x23A00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x55302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x62800
+#define SS_RESET_OCCURRED 0x62900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x53900
+#define SS_UNRECOVERED_READ_ERROR 0x31100
+#define SS_WRITE_ERROR 0x30C02
+#define SS_WRITE_PROTECTED 0x72700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+enum ums_state {
+ UMS_STATE_NORMAL = 0,
+ UMS_STATE_ABORT_BULK_OUT,
+ UMS_STATE_PROTOCOL_RESET,
+ UMS_STATE_EXIT,
+ UMS_STATE_TERMINATED
+};
+
+enum ums_result {
+ UMS_RES_OK = 0,
+ UMS_RES_IO_ERROR = -5,
+ UMS_RES_TIMEOUT = -3,
+ UMS_RES_PROT_FATAL = -4,
+ UMS_RES_INVALID_ARG = -22
+};
+
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+enum buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+typedef struct _bulk_recv_pkt_t {
+ u32 Signature; // 'USBC'.
+ u32 Tag; // Unique per command id.
+ u32 DataTransferLength; // Size of the data.
+ u8 Flags; // Direction in bit 7.
+ u8 Lun; // LUN (normally 0).
+ u8 Length; // Of the CDB, <= SCSI_MAX_CMD_SZ.
+ u8 CDB[16]; // Command Data Block.
+} bulk_recv_pkt_t;
+
+typedef struct _bulk_send_pkt_t {
+ u32 Signature; // 'USBS'.
+ u32 Tag; // Same as original command.
+ u32 Residue; // Amount not transferred.
+ u8 Status;
+} bulk_send_pkt_t;
+
+typedef struct _logical_unit_t
+{
+ sdmmc_t *sdmmc;
+ sdmmc_storage_t *storage;
+
+ u32 num_sectors;
+ u32 offset;
+
+ int unmounted;
+
+ u32 ro;
+ u32 type;
+ u32 partition;
+ u32 removable;
+ u32 prevent_medium_removal;
+
+ u32 info_valid;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+} logical_unit_t;
+
+typedef struct _bulk_ctxt_t {
+ u32 bulk_in;
+ int bulk_in_status;
+ u32 bulk_in_length;
+ u32 bulk_in_length_actual;
+ u8 *bulk_in_buf;
+ enum buffer_state bulk_in_buf_state;
+
+ u32 bulk_out;
+ int bulk_out_status;
+ u32 bulk_out_length;
+ u32 bulk_out_length_actual;
+ int bulk_out_ignore;
+ u8 *bulk_out_buf;
+ enum buffer_state bulk_out_buf_state;
+} bulk_ctxt_t;
+
+typedef struct _usbd_gadget_ums_t {
+ bulk_ctxt_t bulk_ctxt;
+
+ int cmnd_size;
+ u8 cmnd[SCSI_MAX_CMD_SZ];
+
+ u32 lun_idx; // lun index
+ logical_unit_t lun;
+
+ enum ums_state state; // For exception handling.
+
+ enum data_direction data_dir;
+ u32 data_size;
+ u32 data_size_from_cmnd;
+ u32 tag;
+ u32 residue;
+ u32 usb_amount_left;
+
+ u32 phase_error;
+ u32 short_packet_received;
+
+ int thread_wakeup_needed;
+ int can_stall;
+
+ u32 timeouts;
+ bool xusb;
+
+ void (*system_maintenance)(bool);
+ void *label;
+ void (*set_text)(void *, const char *);
+} usbd_gadget_ums_t;
+
+static usb_ops_t usb_ops;
+
+static inline void put_array_le_to_be16(u16 val, void *p)
+{
+ u8 *_p = p;
+ _p[0] = val >> 8;
+ _p[1] = val;
+}
+
+static inline void put_array_le_to_be32(u32 val, void *p)
+{
+ u8 *_p = p;
+ _p[0] = val >> 24;
+ _p[1] = val >> 16;
+ _p[2] = val >> 8;
+ _p[3] = val;
+}
+
+static inline u16 get_array_be_to_le16(const void *p)
+{
+ const u8 *_p = p;
+ u16 val = _p[0] << 8 | _p[1];
+ return val;
+}
+
+static inline u32 get_array_be_to_le24(const void *p)
+{
+ const u8 *_p = p;
+ u32 val = (_p[0] << 16) | (_p[1] << 8) | _p[2];
+ return val;
+}
+
+static inline u32 get_array_be_to_le32(const void *p)
+{
+ const u8 *_p = p;
+ u32 val = (_p[0] << 24) | (_p[1] << 16) | (_p[2] << 8) | _p[3];
+ return val;
+}
+
+static void raise_exception(usbd_gadget_ums_t *ums, enum ums_state new_state)
+{
+ /* Do nothing if a higher-priority exception is already in progress.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal. */
+ if (ums->state <= new_state) {
+ ums->state = new_state;
+ ums->thread_wakeup_needed = 1;
+ }
+}
+
+static void ums_handle_ep0_ctrl(usbd_gadget_ums_t *ums)
+{
+ if (usb_ops.usbd_handle_ep0_ctrl_setup())
+ raise_exception(ums, UMS_STATE_PROTOCOL_RESET);
+}
+
+static int ums_wedge_bulk_in_endpoint(usbd_gadget_ums_t *ums)
+{
+ /* usbd_set_ep_wedge(bulk_ctxt->bulk_in); */
+
+ return UMS_RES_OK;
+}
+
+static int ums_set_stall(u32 ep)
+{
+ usb_ops.usbd_set_ep_stall(ep, USB_EP_CFG_STALL);
+
+ return UMS_RES_OK;
+}
+
+static int ums_clear_stall(u32 ep)
+{
+ usb_ops.usbd_set_ep_stall(ep, USB_EP_CFG_CLEAR);
+
+ return UMS_RES_OK;
+}
+
+static void ums_flush_endpoint(u32 ep)
+{
+ if (usb_ops.usbd_flush_endpoint)
+ usb_ops.usbd_flush_endpoint(ep);
+}
+
+static void _ums_transfer_start(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt, u32 ep, u32 sync_timeout)
+{
+ if (ep == bulk_ctxt->bulk_in)
+ {
+ bulk_ctxt->bulk_in_status = usb_ops.usb_device_ep1_in_write(
+ bulk_ctxt->bulk_in_buf, bulk_ctxt->bulk_in_length,
+ &bulk_ctxt->bulk_in_length_actual, sync_timeout);
+
+ if (bulk_ctxt->bulk_in_status == USB_ERROR_XFER_ERROR)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# EP IN transfer!");
+ ums_flush_endpoint(bulk_ctxt->bulk_in);
+ }
+ else if (bulk_ctxt->bulk_in_status == USB2_ERROR_XFER_NOT_ALIGNED)
+ ums->set_text(ums->label, "#C7EA46 Error:# EP IN Buffer not aligned!");
+
+ if (sync_timeout)
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_EMPTY;
+ }
+ else
+ {
+ bulk_ctxt->bulk_out_status = usb_ops.usb_device_ep1_out_read(
+ bulk_ctxt->bulk_out_buf, bulk_ctxt->bulk_out_length,
+ &bulk_ctxt->bulk_out_length_actual, sync_timeout);
+
+ if (bulk_ctxt->bulk_out_status == USB_ERROR_XFER_ERROR)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# EP OUT transfer!");
+ ums_flush_endpoint(bulk_ctxt->bulk_out);
+ }
+ else if (bulk_ctxt->bulk_out_status == USB2_ERROR_XFER_NOT_ALIGNED)
+ ums->set_text(ums->label, "#C7EA46 Error:# EP OUT Buffer not aligned!");
+
+ if (sync_timeout)
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_FULL;
+ }
+}
+
+static void _ums_transfer_out_big_read(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ bulk_ctxt->bulk_out_status = usb_ops.usb_device_ep1_out_read_big(
+ bulk_ctxt->bulk_out_buf, bulk_ctxt->bulk_out_length,
+ &bulk_ctxt->bulk_out_length_actual);
+
+ if (bulk_ctxt->bulk_out_status == USB_ERROR_XFER_ERROR)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# EP OUT transfer!");
+ ums_flush_endpoint(bulk_ctxt->bulk_out);
+ }
+
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_FULL;
+}
+
+static void _ums_transfer_finish(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt, u32 ep)
+{
+ if (ep == bulk_ctxt->bulk_in)
+ {
+ bulk_ctxt->bulk_in_status = usb_ops.usb_device_ep1_in_writing_finish(
+ &bulk_ctxt->bulk_in_length_actual);
+
+ if (bulk_ctxt->bulk_in_status == USB_ERROR_XFER_ERROR)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# EP IN transfer!");
+ ums_flush_endpoint(bulk_ctxt->bulk_in);
+ }
+
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_EMPTY;
+ }
+ else
+ {
+ bulk_ctxt->bulk_out_status = usb_ops.usb_device_ep1_out_reading_finish(
+ &bulk_ctxt->bulk_out_length_actual);
+
+ if (bulk_ctxt->bulk_out_status == USB_ERROR_XFER_ERROR)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# EP OUT transfer!");
+ ums_flush_endpoint(bulk_ctxt->bulk_out);
+ }
+
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_FULL;
+ }
+}
+
+static void _ums_reset_buffer(bulk_ctxt_t *bulk_ctxt, u32 ep)
+{
+ if (ep == bulk_ctxt->bulk_in)
+ bulk_ctxt->bulk_in_buf = (u8 *)USB_EP_BULK_IN_BUF_ADDR;
+ else
+ bulk_ctxt->bulk_out_buf = (u8 *)USB_EP_BULK_OUT_BUF_ADDR;
+}
+
+/*
+ * The following are old data based on max 64KB SCSI transfers.
+ * The endpoint xfer is actually 41.2 MB/s and SD card max 39.2 MB/s, with higher SCSI
+ * transfers, but the concurrency still helps and increases speeds by 20%.
+ *
+ * Concurrency of the SDMMC and USB xfers is very important with no cache.
+ * The worst offender being the SD card. We are already limited by bus, so
+ * concurrency helps minimize the SDMMC overhead.
+ * Max achieved bulk endpoint rate on a Tegra X1 and USB2.0 is 39.4 MB/s.
+ *
+ * USB bulk endpoint raw max transfer rate:
+ * 39.4MB/S - SCSI 128KB.
+ * 38.2MB/s - SCSI 64KB.
+ *
+ * 128 KB, 64 KB, 32 KB, 16 KB, 8 KB - Internal SDMMC I\O Sizes
+ * -------------------------------------------------------------------------------------
+ * eMMC - Toshiba - 4MB reads: 314.8 MB/s:
+ * 225.9 MB/s, 168.6 MB/s, 114.7 MB/s, 86.4 MB/s, 50.3 MB/s - RAW SDMMC.
+ * 33.5 MB/s, 31.9 MB/s, 29.3 MB/s, 27.1 MB/s, 22.1 MB/s - SCSI 128KB, No concurrency.
+ * 33.5 MB/s, 35.3 MB/s, 36.3 MB/s, 37.3 MB/s, 37.8 MB/s - SCSI 128KB, Concurrency.
+ * --.- --/-, 31.1 MB/s, 28.7 MB/s, 26.5 MB/s, 21.7 MB/s - SCSI 64KB, No concurrency.
+ * --.- --/-, 31.1 MB/s, 32.7 MB/s, 34.4 MB/s, 35.0 MB/s - SCSI 64KB, Concurrency.
+ *
+ * SD Card - Samsung Evo+ 128GB - 4MB reads: 91.6 MB/s:
+ * 72.6 MB/s, 62.8 MB/s, 47.4 MB/s, 31.1 MB/s, 18.5 MB/s - RAW SDMMC.
+ * 25.5 MB/s, 24.2 MB/s, 21.5 MB/s, 17.4 MB/s, 12.6 MB/s - SCSI 128KB, No concurrency.
+ * 25.5 MB/s, 30.0 MB/s, 32.6 MB/s, 28.3 MB/s, 18.0 MB/s - SCSI 128KB, Concurrency.
+ * --.- --/-, 23.8 MB/s, 21.2 MB/s, 17.1 MB/s, 12.5 MB/s - SCSI 64KB, No concurrency.
+ * --.- --/-, 23.8 MB/s, 27.2 MB/s, 25.8 MB/s, 17.5 MB/s - SCSI 64KB, Concurrency.
+ */
+
+static int _scsi_read(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u32 lba_offset;
+ bool first_read = true;
+ u8 *sdmmc_buf = (u8 *)SDXC_BUF_ALIGNED;
+
+ // Get the starting LBA and check that it's not too big.
+ if (ums->cmnd[0] == SC_READ_6)
+ lba_offset = get_array_be_to_le24(&ums->cmnd[1]);
+ else
+ {
+ lba_offset = get_array_be_to_le32(&ums->cmnd[2]);
+
+ // We allow DPO and FUA bypass cache bits, but we don't use them.
+ if ((ums->cmnd[1] & ~0x18) != 0)
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+ }
+ if (lba_offset >= ums->lun.num_sectors)
+ {
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ // Check that request data size is not 0.
+ u32 amount_left = ums->data_size_from_cmnd >> UMS_DISK_LBA_SHIFT;
+ if (!amount_left)
+ return UMS_RES_IO_ERROR; // No default reply.
+
+ // Limit IO transfers based on request for faster concurrent reads.
+ u32 max_io_transfer = (amount_left >= UMS_SCSI_TRANSFER_512K) ?
+ UMS_DISK_MAX_IO_TRANSFER_64K : UMS_DISK_MAX_IO_TRANSFER_32K;
+
+ while (true)
+ {
+ // Max io size and end sector limits.
+ u32 amount = MIN(amount_left, max_io_transfer);
+ amount = MIN(amount, ums->lun.num_sectors - lba_offset);
+
+ // Check if it is a read past the end sector.
+ if (!amount)
+ {
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ bulk_ctxt->bulk_in_length = 0;
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_FULL;
+ break;
+ }
+
+ // Do the SDMMC read.
+ if (!sdmmc_storage_read(ums->lun.storage, ums->lun.offset + lba_offset, amount, sdmmc_buf))
+ amount = 0;
+
+ // Wait for the async USB transfer to finish.
+ if (!first_read)
+ _ums_transfer_finish(ums, bulk_ctxt, bulk_ctxt->bulk_in);
+
+ lba_offset += amount;
+ amount_left -= amount;
+ ums->residue -= amount << UMS_DISK_LBA_SHIFT;
+
+ bulk_ctxt->bulk_in_length = amount << UMS_DISK_LBA_SHIFT;
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_FULL;
+ bulk_ctxt->bulk_in_buf = sdmmc_buf;
+
+ // If an error occurred, report it and its position.
+ if (!amount)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# SDMMC Read!");
+ ums->lun.sense_data = SS_UNRECOVERED_READ_ERROR;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ break;
+ }
+
+ // Last SDMMC read. Last part will be sent by the finish reply function.
+ if (!amount_left)
+ break;
+
+ // Start the USB transfer.
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_START);
+ first_read = false;
+
+ // Increment our buffer to read new data.
+ sdmmc_buf += amount << UMS_DISK_LBA_SHIFT;
+ }
+
+ return UMS_RES_IO_ERROR; // No default reply.
+}
+
+/*
+ * Writes are another story.
+ * Tests showed that big writes are faster than concurrent 32K usb reads + writes.
+ * The only thing that can help here is caching the writes. But for the simplicity
+ * of this implementation it will not be implemented yet.
+ */
+
+static int _scsi_write(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ static char txt_buf[256];
+ u32 amount_left_to_req, amount_left_to_write;
+ u32 usb_lba_offset, lba_offset;
+ u32 amount;
+
+ if (ums->lun.ro)
+ {
+ ums->lun.sense_data = SS_WRITE_PROTECTED;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ if (ums->cmnd[0] == SC_WRITE_6)
+ lba_offset = get_array_be_to_le24(&ums->cmnd[1]);
+ else
+ {
+ lba_offset = get_array_be_to_le32(&ums->cmnd[2]);
+
+ // We allow DPO and FUA bypass cache bits. We only implement FUA by performing synchronous output.
+ if (ums->cmnd[1] & ~0x18)
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+ }
+
+ // Check that starting LBA is not past the end sector offset.
+ if (lba_offset >= ums->lun.num_sectors)
+ {
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Carry out the file writes */
+ usb_lba_offset = lba_offset;
+ amount_left_to_req = ums->data_size_from_cmnd;
+ amount_left_to_write = ums->data_size_from_cmnd;
+
+ while (amount_left_to_write > 0)
+ {
+
+ /* Queue a request for more data from the host */
+ if (amount_left_to_req)
+ {
+
+ // Limit write to max supported read from EP OUT.
+ amount = MIN(amount_left_to_req, UMS_EP_OUT_MAX_XFER);
+
+ if (usb_lba_offset >= ums->lun.num_sectors) //////////Check if it works with concurrency
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# Write - Past last sector!");
+ amount_left_to_req = 0;
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ ums->lun.sense_data_info = usb_lba_offset;
+ ums->lun.info_valid = 1;
+ continue;
+ }
+
+ // Get the next buffer.
+ usb_lba_offset += amount >> UMS_DISK_LBA_SHIFT;
+ ums->usb_amount_left -= amount;
+ amount_left_to_req -= amount;
+
+ bulk_ctxt->bulk_out_length = amount;
+
+ _ums_transfer_out_big_read(ums, bulk_ctxt);
+ }
+
+ if (bulk_ctxt->bulk_out_buf_state == BUF_STATE_FULL)
+ {
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_EMPTY;
+
+ // Did something go wrong with the transfer?.
+ if (bulk_ctxt->bulk_out_status != 0)
+ {
+ ums->lun.sense_data = SS_COMMUNICATION_FAILURE;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ s_printf(txt_buf, "#C7EA46 Error:# Write - Comm failure %d!", bulk_ctxt->bulk_out_status);
+ ums->set_text(ums->label, txt_buf);
+ break;
+ }
+
+ amount = bulk_ctxt->bulk_out_length_actual;
+
+ if ((ums->lun.num_sectors - lba_offset) < (amount >> UMS_DISK_LBA_SHIFT))
+ {
+ DPRINTF("write %X @ %X beyond end %X\n", amount, lba_offset, ums->lun.num_sectors);
+ amount = (ums->lun.num_sectors - lba_offset) << UMS_DISK_LBA_SHIFT;
+ }
+
+ /*
+ * Don't accept excess data. The spec doesn't say
+ * what to do in this case. We'll ignore the error.
+ */
+ amount = MIN(amount, bulk_ctxt->bulk_out_length);
+
+ /* Don't write a partial block */
+ amount -= (amount & 511);
+ if (amount == 0)
+ goto empty_write;
+
+ /* Perform the write */
+ if (!sdmmc_storage_write(ums->lun.storage, ums->lun.offset + lba_offset,
+ amount >> UMS_DISK_LBA_SHIFT, (u8 *)bulk_ctxt->bulk_out_buf))
+ amount = 0;
+
+DPRINTF("file write %X @ %X\n", amount, lba_offset);
+
+ lba_offset += amount >> UMS_DISK_LBA_SHIFT;
+ amount_left_to_write -= amount;
+ ums->residue -= amount;
+
+ /* If an error occurred, report it and its position */
+ if (!amount)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# SDMMC Write!");
+ ums->lun.sense_data = SS_WRITE_ERROR;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ break;
+ }
+
+ empty_write:
+ // Did the host decide to stop early?
+ if (bulk_ctxt->bulk_out_length_actual < bulk_ctxt->bulk_out_length)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# Empty Write!");
+ ums->short_packet_received = 1;
+ break;
+ }
+ }
+ }
+
+ return UMS_RES_IO_ERROR; // No default reply.
+}
+
+static int _scsi_verify(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ // Check that start LBA is past the end sector offset.
+ u32 lba_offset = get_array_be_to_le32(&ums->cmnd[2]);
+ if (lba_offset >= ums->lun.num_sectors)
+ {
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ // We allow DPO but we don't implement it. Check that nothing else is enabled.
+ if (ums->cmnd[1] & ~0x10)
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ u32 verification_length = get_array_be_to_le16(&ums->cmnd[7]);
+ if (verification_length == 0)
+ return UMS_RES_IO_ERROR; // No default reply.
+
+ u32 amount;
+ while (verification_length > 0)
+ {
+
+ // Limit to EP buffer size and end sector offset.
+ amount = MIN(verification_length, USB_EP_BUFFER_MAX_SIZE >> UMS_DISK_LBA_SHIFT);
+ amount = MIN(amount, ums->lun.num_sectors - lba_offset);
+ if (amount == 0) {
+ ums->lun.sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ break;
+ }
+
+ if (!sdmmc_storage_read(ums->lun.storage, ums->lun.offset + lba_offset, amount, bulk_ctxt->bulk_in_buf))
+ amount = 0;
+
+DPRINTF("File read %X @ %X\n", amount, lba_offset);
+
+ if (!amount)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# File verify!");
+ ums->lun.sense_data = SS_UNRECOVERED_READ_ERROR;
+ ums->lun.sense_data_info = lba_offset;
+ ums->lun.info_valid = 1;
+ break;
+ }
+ lba_offset += amount;
+ verification_length -= amount;
+ }
+ return UMS_RES_OK;
+}
+
+static int _scsi_inquiry(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+
+ memset(buf, 0, 36);
+
+ // Enable Vital Product Data (EVPD) and Unit Serial Number.
+ if (ums->cmnd[1] == 1 && ums->cmnd[2] == 0x80)
+ {
+ buf[0] = 0;
+ buf[1] = ums->cmnd[2];
+ buf[2] = 0;
+ buf[3] = 20; // Additional length.
+
+ buf += 4;
+ s_printf((char *)buf, "%04X%s",
+ ums->lun.storage->cid.serial, ums->lun.type == MMC_SD ? " SD " : " eMMC ");
+
+ switch (ums->lun.partition)
+ {
+ case 0:
+ strcpy((char *)buf + strlen((char *)buf), "RAW");
+ break;
+ case EMMC_GPP + 1:
+ s_printf((char *)buf + strlen((char *)buf), "GPP");
+ break;
+ case EMMC_BOOT0 + 1:
+ s_printf((char *)buf + strlen((char *)buf), "BOOT0");
+ break;
+ case EMMC_BOOT1 + 1:
+ s_printf((char *)buf + strlen((char *)buf), "BOOT1");
+ break;
+ }
+
+ for (u32 i = strlen((char *)buf); i < 20; i++)
+ buf[i] = ' ';
+
+ return 24;
+ }
+ else /* if (ums->cmnd[1] == 0 && ums->cmnd[2] == 0) */ // Standard inquiry.
+ {
+ buf[0] = SCSI_TYPE_DISK;
+ buf[1] = ums->lun.removable ? 0x80 : 0;
+ buf[2] = 6; // ANSI INCITS 351-2001 (SPC-2).////////SPC2: 4, SPC4: 6
+ buf[3] = 2; // SCSI-2 INQUIRY data format.
+ buf[4] = 31; // Additional length.
+ // buf5-7: No special options.
+
+ // Vendor ID. Max 8 chars.
+ buf += 8;
+ strcpy((char *)buf, "hekate");
+
+ // Product ID. Max 16 chars.
+ buf += 8;
+ switch (ums->lun.partition)
+ {
+ case 0:
+ s_printf((char *)buf, "%s", "SD RAW");
+ break;
+ case EMMC_GPP + 1:
+ s_printf((char *)buf, "%s%s",
+ ums->lun.type == MMC_SD ? "SD " : "eMMC ", "GPP");
+ break;
+ case EMMC_BOOT0 + 1:
+ s_printf((char *)buf, "%s%s",
+ ums->lun.type == MMC_SD ? "SD " : "eMMC ", "BOOT0");
+ break;
+ case EMMC_BOOT1 + 1:
+ s_printf((char *)buf, "%s%s",
+ ums->lun.type == MMC_SD ? "SD " : "eMMC ", "BOOT1");
+ break;
+ }
+
+ // Rev ID. Max 4 chars.
+ buf += 16;
+ strcpy((char *)buf, "1.00");
+
+ return 36;
+ }
+}
+
+static int _scsi_request_sense(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+ u32 sd, sdinfo;
+ int valid;
+
+ sd = ums->lun.sense_data;
+ sdinfo = ums->lun.sense_data_info;
+ valid = ums->lun.info_valid << 7;
+ ums->lun.sense_data = SS_NO_SENSE;
+ ums->lun.sense_data_info = 0;
+ ums->lun.info_valid = 0;
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; // Valid, current error.
+ buf[2] = SK(sd);
+ put_array_le_to_be32(sdinfo, &buf[3]); // Sense information.
+ buf[7] = 18 - 8; // Additional sense length.
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+
+ return 18;
+}
+
+static int _scsi_read_capacity(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+ u32 lba = get_array_be_to_le32(&ums->cmnd[2]);
+ int pmi = ums->cmnd[8];
+
+ // Check the PMI and LBA fields.
+ if (pmi > 1 || (pmi == 0 && lba != 0))
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ put_array_le_to_be32(ums->lun.num_sectors - 1, &buf[0]); // Max logical block.
+ put_array_le_to_be32(UMS_DISK_LBA_SIZE, &buf[4]); // Block length.
+
+ return 8;
+}
+
+static int _scsi_log_sense(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+ u8 *buf0 = buf;
+ bool valid_page = false;
+
+ u8 pc = ums->cmnd[2] >> 6;
+ u8 page_code = ums->cmnd[2] & 0x3F;
+ u8 sub_page_code = ums->cmnd[3];
+
+ if (ums->cmnd[1] & 1)
+ {
+ ums->lun.sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ if (pc != 1) // Current cumulative values.
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ memset(buf, 0, 8);
+ if (page_code == 0x00 && !sub_page_code) // Supported pages.
+ {
+ valid_page = true;
+ buf[0] = 0x00; // Page code.
+ buf += 4;
+
+ buf[0] = 0x00; // Page 0.
+ buf[1] = 0x0D; // Page 1.
+
+ buf += 2;
+ }
+ else if (page_code == 0x0d && !sub_page_code) // Temperature.
+ {
+ valid_page = true;
+ buf[0] = 0x0D;
+ buf += 4;
+
+ put_array_le_to_be16(0, &buf[0]); // Param code.
+ buf[2] = 1; // Param control byte.
+ buf[3] = 2; // Param length.
+ buf[4] = 0; // Reserved.
+ buf[5] = 35; // Temperature (C) current (PCB here).
+
+ put_array_le_to_be16(0, &buf[6]); // PARAMETER CODE
+ buf[8] = 1; // Param control byte.
+ buf[9] = 2; // Param length.
+ buf[10] = 0; // Reserved.
+ buf[11] = 60; // Temperature (C) reference.
+
+ buf += 12;
+ }
+
+ // Check that a valid page mode data length was requested.
+ u32 len = buf - buf0;
+ if (!valid_page)
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ put_array_le_to_be16(len - 4, &buf0[2]);
+
+ return len;
+}
+
+static int _scsi_mode_sense(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+ u8 *buf0 = buf;
+ bool valid_page = false;
+
+ u8 pc = ums->cmnd[2] >> 6;
+ u8 page_code = ums->cmnd[2] & 0x3F;
+ bool changeable_values = pc == 1;
+ bool all_pages = page_code == 0x3F;
+
+ if ((ums->cmnd[1] & ~0x08) != 0) // Mask away DBD.
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ if (pc == 3)
+ {
+ ums->lun.sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later. */
+ memset(buf, 0, 8);
+ if (ums->cmnd[0] == SC_MODE_SENSE_6)
+ {
+ buf[2] = (ums->lun.ro ? 0x80 : 0x00); // WP, DPOFUA.
+ buf += 4;
+ }
+ else // SC_MODE_SENSE_10.
+ {
+ buf[3] = (ums->lun.ro ? 0x80 : 0x00); // WP, DPOFUA.
+ buf += 8;
+ }
+
+ // The only page we support is the Caching page.
+ // What about x1C
+ if (page_code == 0x08 || all_pages)
+ {
+ valid_page = true;
+ buf[0] = 0x08; // Page code.
+ buf[1] = 18; // Page length.
+ memset(buf + 2, 0, 18); // Set all parameters to 0.
+
+ // None of the fields are changeable.
+ if (!changeable_values)
+ {
+ // Write Cache enable, Read Cache not disabled, Multiplication Factor off.
+ buf[2] = 0x04;
+
+ // Multiplication Factor is disabled, so all values below are 1x LBA.
+ put_array_le_to_be16(0xFFFF, &buf[4]); // Disable Prefetch if >32MB.
+ put_array_le_to_be16(0x0000, &buf[6]); // Minimum Prefetch 0MB.
+ put_array_le_to_be16(0xFFFF, &buf[8]); // Maximum Prefetch 32MB.
+ put_array_le_to_be16(0xFFFF, &buf[10]); // Maximum Prefetch ceiling 32MB.
+ }
+
+ buf += 20;
+ }
+
+ // Check that a valid page mode data length was requested.
+ u32 len = buf - buf0;
+ if (!valid_page)
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Store the mode data length */
+ if (ums->cmnd[0] == SC_MODE_SENSE_6)
+ buf0[0] = len - 1;
+ else
+ put_array_le_to_be16(len - 2, buf0);
+
+ return len;
+}
+
+static int _scsi_start_stop(usbd_gadget_ums_t *ums)
+{
+ int loej, start;
+
+ if (!ums->lun.removable)
+ {
+ ums->lun.sense_data = SS_INVALID_COMMAND;
+
+ return UMS_RES_INVALID_ARG;
+ }
+ else if ((ums->cmnd[1] & ~0x01) != 0 || // Mask away Immed.
+ (ums->cmnd[4] & ~0x03) != 0) // Mask LoEj, Start.
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ loej = ums->cmnd[4] & 0x02;
+ start = ums->cmnd[4] & 0x01;
+
+ // We do not support re-mounting.
+ if (start)
+ {
+ if (ums->lun.unmounted)
+ {
+ ums->lun.sense_data = SS_MEDIUM_NOT_PRESENT;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ return UMS_RES_OK;
+ }
+
+ // Check if we are allowed to unload the media.
+ if (ums->lun.prevent_medium_removal)
+ {
+ ums->set_text(ums->label, "#C7EA46 Status:# Unload attempt prevented");
+ ums->lun.sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ if (!loej)
+ return UMS_RES_OK;
+
+ // Unmount means we exit UMS because of ejection.
+ ums->lun.unmounted = 1;
+
+ return UMS_RES_OK;
+}
+
+static int _scsi_prevent_allow_removal(usbd_gadget_ums_t *ums)
+{
+ int prevent;
+
+ if (!ums->lun.removable)
+ {
+ ums->lun.sense_data = SS_INVALID_COMMAND;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ prevent = ums->cmnd[4] & 0x01;
+ if ((ums->cmnd[4] & ~0x01) != 0) // Mask away Prevent.
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ // Notify for possible unmounting?
+ // Normally we sync here but we do synced writes to SDMMC.
+ if (ums->lun.prevent_medium_removal && !prevent)
+ ;
+
+ ums->lun.prevent_medium_removal = prevent;
+
+ return UMS_RES_OK;
+}
+
+static int _scsi_read_format_capacities(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 *buf = (u8 *)bulk_ctxt->bulk_in_buf;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; // Only the Current/Maximum Capacity Descriptor.
+ buf += 4;
+
+ put_array_le_to_be32(ums->lun.num_sectors, &buf[0]); // Number of blocks.
+ put_array_le_to_be32(UMS_DISK_LBA_SIZE, &buf[4]); // Block length.
+ buf[4] = 0x02; // Current capacity.
+
+ return 12;
+}
+
+// Check whether the command is properly formed and whether its data size
+// and direction agree with the values we already have.
+static int _ums_check_scsi_cmd(usbd_gadget_ums_t *ums, int cmnd_size,
+ enum data_direction data_dir, u32 mask, int needs_medium)
+{
+//const char dirletter[4] = {'u', 'o', 'i', 'n'};
+DPRINTF("SCSI command: %X; Dc=%d, D%c=%X; Hc=%d, H%c=%X\n",
+ ums->cmnd[0], cmnd_size, dirletter[(int)ums->data_dir],
+ ums->data_size_from_cmnd, ums->cmnd_size,
+ dirletter[(int)data_dir], ums->data_size);
+
+ // We can't reply if we don't know the direction and size.
+ if (ums->data_size_from_cmnd == 0)
+ data_dir = DATA_DIR_NONE;
+
+ // This is a phase error but we continue and only transfer as much we can.
+ if (ums->data_size < ums->data_size_from_cmnd)
+ {
+ ums->data_size_from_cmnd = ums->data_size;
+ ums->phase_error = 1;
+ }
+
+ ums->residue = ums->data_size;
+ ums->usb_amount_left = ums->data_size;
+
+ if (ums->data_dir != data_dir && ums->data_size_from_cmnd > 0)
+ {
+ ums->phase_error = 1;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ // Cmd length verification.
+ if (cmnd_size != ums->cmnd_size)
+ {
+
+ // Special case workaround for Windows and Xbox 360.
+ if (cmnd_size <= ums->cmnd_size)
+ cmnd_size = ums->cmnd_size;
+ else
+ {
+ ums->phase_error = 1;
+
+ return UMS_RES_INVALID_ARG;
+ }
+ }
+
+ // check that LUN ums->cmnd[1] >> 5 is 0 because of only one.
+
+ if (ums->cmnd[0] != SC_REQUEST_SENSE)
+ {
+ ums->lun.sense_data = SS_NO_SENSE;
+ ums->lun.sense_data_info = 0;
+ ums->lun.info_valid = 0;
+ }
+
+ // If a unit attention condition exists, only INQUIRY and REQUEST SENSE
+ // commands are allowed.
+ if (ums->lun.unit_attention_data != SS_NO_SENSE && ums->cmnd[0] != SC_INQUIRY &&
+ ums->cmnd[0] != SC_REQUEST_SENSE)
+ {
+ ums->lun.sense_data = ums->lun.unit_attention_data;
+ ums->lun.unit_attention_data = SS_NO_SENSE;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ // Check that only command bytes listed in the mask are set.
+ ums->cmnd[1] &= 0x1F; // Mask away the LUN.
+ for (u32 i = 1; i < cmnd_size; ++i)
+ {
+ if (ums->cmnd[i] && !(mask & BIT(i)))
+ {
+ ums->lun.sense_data = SS_INVALID_FIELD_IN_CDB;
+
+ return UMS_RES_INVALID_ARG;
+ }
+ }
+
+ // If the medium isn't mounted and the command needs to access it, return an error.
+ if (ums->lun.unmounted && needs_medium)
+ {
+ ums->lun.sense_data = SS_MEDIUM_NOT_PRESENT;
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ return UMS_RES_OK;
+}
+
+static int _ums_parse_scsi_cmd(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u32 len;
+ int reply = UMS_RES_INVALID_ARG;
+
+ ums->phase_error = 0;
+ ums->short_packet_received = 0;
+
+ switch (ums->cmnd[0])
+ {
+ case SC_INQUIRY:
+ ums->data_size_from_cmnd = ums->cmnd[4];
+ u32 mask = (1<<4);
+ if (ums->cmnd[1] == 1 && ums->cmnd[2] == 0x80) // Inquiry S/N.
+ mask = (1<<1) | (1<<2) | (1<<4);
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_TO_HOST, mask, 0);
+ if (reply == 0)
+ reply = _scsi_inquiry(ums, bulk_ctxt);
+ break;
+
+ case SC_LOG_SENSE:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]);
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (3<<7), 0);
+ if (reply == 0)
+ reply = _scsi_log_sense(ums, bulk_ctxt);
+ break;
+
+ case SC_MODE_SELECT_6:
+ ums->data_size_from_cmnd = ums->cmnd[4];
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_FROM_HOST, (1<<1) | (1<<4), 0);
+ if (reply == 0)
+ {
+ // We don't support MODE SELECT.
+ ums->lun.sense_data = SS_INVALID_COMMAND;
+ reply = UMS_RES_INVALID_ARG;
+ }
+ break;
+
+ case SC_MODE_SELECT_10:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]);
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_FROM_HOST, (1<<1) | (3<<7), 0);
+ if (reply == 0)
+ {
+ // We don't support MODE SELECT.
+ ums->lun.sense_data = SS_INVALID_COMMAND;
+ reply = UMS_RES_INVALID_ARG;
+ }
+ break;
+
+ case SC_MODE_SENSE_6:
+ ums->data_size_from_cmnd = ums->cmnd[4];
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (1<<4), 0);
+ if (reply == 0)
+ reply = _scsi_mode_sense(ums, bulk_ctxt);
+ break;
+
+ case SC_MODE_SENSE_10:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]);
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_TO_HOST, (1<<1) | (1<<2) | (3<<7), 0);
+ if (reply == 0)
+ reply = _scsi_mode_sense(ums, bulk_ctxt);
+ break;
+
+ case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_NONE, (1<<4), 0);
+ if (reply == 0)
+ reply = _scsi_prevent_allow_removal(ums);
+ break;
+
+ case SC_READ_6:
+ len = ums->cmnd[4];
+ ums->data_size_from_cmnd = (len == 0 ? 256 : len) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1);
+ if (reply == 0)
+ reply = _scsi_read(ums, bulk_ctxt);
+ break;
+
+ case SC_READ_10:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1);
+ if (reply == 0)
+ reply = _scsi_read(ums, bulk_ctxt);
+ break;
+
+ case SC_READ_12:
+ ums->data_size_from_cmnd = get_array_be_to_le32(&ums->cmnd[6]) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1);
+ if (reply == 0)
+ reply = _scsi_read(ums, bulk_ctxt);
+ break;
+
+ case SC_READ_CAPACITY:
+ ums->data_size_from_cmnd = 8;
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_TO_HOST, (0xf<<2) | (1<<8), 1);
+ if (reply == 0)
+ reply = _scsi_read_capacity(ums, bulk_ctxt);
+ break;
+ case SC_READ_FORMAT_CAPACITIES:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]);
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_TO_HOST, (3<<7), 1);
+ if (reply == 0)
+ reply = _scsi_read_format_capacities(ums, bulk_ctxt);
+ break;
+
+ case SC_REQUEST_SENSE:
+ ums->data_size_from_cmnd = ums->cmnd[4];
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_TO_HOST, (1<<4), 0);
+ if (reply == 0)
+ reply = _scsi_request_sense(ums, bulk_ctxt);
+ break;
+
+ case SC_START_STOP_UNIT:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_NONE, (1<<1) | (1<<4), 0);
+ if (reply == 0)
+ reply = _scsi_start_stop(ums);
+ break;
+
+ case SC_SYNCHRONIZE_CACHE:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_NONE, (0xf<<2) | (3<<7), 1);
+ if (reply == 0)
+ reply = 0; // Don't bother
+ break;
+
+ case SC_TEST_UNIT_READY:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_NONE, 0, 1);
+ break;
+
+ // This command is used by Windows. We support a minimal version and BytChk must be 0.
+ case SC_VERIFY:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_NONE, (1<<1) | (0xf<<2) | (3<<7), 1);
+ if (reply == 0)
+ reply = _scsi_verify(ums, bulk_ctxt);
+ break;
+
+ case SC_WRITE_6:
+ len = ums->cmnd[4];
+ ums->data_size_from_cmnd = (len == 0 ? 256 : len) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1);
+ if (reply == 0)
+ reply = _scsi_write(ums, bulk_ctxt);
+ break;
+
+ case SC_WRITE_10:
+ ums->data_size_from_cmnd = get_array_be_to_le16(&ums->cmnd[7]) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1);
+ if (reply == 0)
+ reply = _scsi_write(ums, bulk_ctxt);
+ break;
+
+ case SC_WRITE_12:
+ ums->data_size_from_cmnd = get_array_be_to_le32(&ums->cmnd[6]) << UMS_DISK_LBA_SHIFT;
+ reply = _ums_check_scsi_cmd(ums, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1);
+ if (reply == 0)
+ reply = _scsi_write(ums, bulk_ctxt);
+ break;
+
+ // Mandatory commands that we don't implement. No need.
+ case SC_READ_HEADER:
+ case SC_READ_TOC:
+ case SC_FORMAT_UNIT:
+ case SC_RELEASE:
+ case SC_RESERVE:
+ case SC_SEND_DIAGNOSTIC:
+ default:
+ ums->data_size_from_cmnd = 0;
+ reply = _ums_check_scsi_cmd(ums, ums->cmnd_size, DATA_DIR_UNKNOWN, 0xFF, 0);
+ if (reply == 0)
+ {
+ ums->lun.sense_data = SS_INVALID_COMMAND;
+ reply = UMS_RES_INVALID_ARG;
+ }
+ break;
+ }
+
+ if (reply == UMS_RES_INVALID_ARG)
+ reply = 0; // Error reply length.
+
+ // Set up reply buffer for finish_reply(). Otherwise it's already set.
+ if (reply >= 0 && ums->data_dir == DATA_DIR_TO_HOST)
+ {
+ reply = MIN((u32)reply, ums->data_size_from_cmnd);
+ bulk_ctxt->bulk_in_length = reply;
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_FULL;
+ ums->residue -= reply;
+ }
+
+ return UMS_RES_OK;
+}
+
+static int pad_with_zeros(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_EMPTY; // For the first iteration.
+ u32 current_len_to_keep = bulk_ctxt->bulk_in_length;
+ ums->usb_amount_left = current_len_to_keep + ums->residue;
+
+ while (ums->usb_amount_left > 0)
+ {
+ u32 nsend = MIN(ums->usb_amount_left, USB_EP_BUFFER_MAX_SIZE);
+ memset(bulk_ctxt->bulk_in_buf + current_len_to_keep, 0, nsend - current_len_to_keep);
+ bulk_ctxt->bulk_in_length = nsend;
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
+ ums->usb_amount_left -= nsend;
+ current_len_to_keep = 0;
+ }
+
+ return UMS_RES_OK;
+}
+
+static int throw_away_data(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ if (bulk_ctxt->bulk_out_buf_state != BUF_STATE_EMPTY || ums->usb_amount_left > 0)
+ {
+ // Try to submit another request if we need one.
+ if (bulk_ctxt->bulk_out_buf_state == BUF_STATE_EMPTY && ums->usb_amount_left > 0)
+ {
+ u32 amount = MIN(ums->usb_amount_left, USB_EP_BUFFER_MAX_SIZE);
+
+ bulk_ctxt->bulk_out_length = amount;
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED_DATA);
+ ums->usb_amount_left -= amount;
+
+ return UMS_RES_OK;
+ }
+
+ // Throw away the data in a filled buffer.
+ if (bulk_ctxt->bulk_out_buf_state == BUF_STATE_FULL)
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_EMPTY;
+
+ // A short packet or an error ends everything.
+ if (bulk_ctxt->bulk_out_length_actual != bulk_ctxt->bulk_out_length ||
+ bulk_ctxt->bulk_out_status != USB_RES_OK)
+ {
+ raise_exception(ums, UMS_STATE_ABORT_BULK_OUT);
+ return UMS_RES_PROT_FATAL;
+ }
+ }
+ return UMS_RES_OK;
+}
+
+static int finish_reply(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ int rc = UMS_RES_OK;
+
+ switch (ums->data_dir) {
+ case DATA_DIR_NONE:
+ break; // Nothing to send.
+
+ // If this is a CB or CBI with an unknown command, we mustn't
+ // try to send or receive any data. Stall if we can and wait reset.
+ case DATA_DIR_UNKNOWN:
+ if (ums->can_stall)
+ {
+ ums_set_stall(bulk_ctxt->bulk_out);
+ rc = ums_set_stall(bulk_ctxt->bulk_in);
+ ums->set_text(ums->label, "#C7EA46 Error:# Direction unknown. Stalled both EP!");
+ } // Else do nothing.
+ break;
+
+ // All but the last buffer of data have already been sent.
+ case DATA_DIR_TO_HOST:
+ if (ums->data_size)
+ {
+ // If there's no residue, simply send the last buffer.
+ if (!ums->residue)
+ {
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
+
+ /* For Bulk-only, if we're allowed to stall then send the
+ * short packet and halt the bulk-in endpoint. If we can't
+ * stall, pad out the remaining data with 0's. */
+ }
+ else if (ums->can_stall)
+ {
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
+ rc = ums_set_stall(bulk_ctxt->bulk_in);
+ ums->set_text(ums->label, "#C7EA46 Error:# Residue. Stalled EP IN!");
+ }
+ else
+ rc = pad_with_zeros(ums, bulk_ctxt);
+ }
+
+ // In case we used SDMMC transfer, reset the buffer address.
+ _ums_reset_buffer(bulk_ctxt, bulk_ctxt->bulk_in);
+ break;
+
+ // We have processed all we want from the data the host has sent.
+ // There may still be outstanding bulk-out requests.
+ case DATA_DIR_FROM_HOST:
+ if (ums->residue)
+ {
+ if (ums->short_packet_received) // Did the host stop sending unexpectedly early?
+ {
+ raise_exception(ums, UMS_STATE_ABORT_BULK_OUT);
+ rc = UMS_RES_PROT_FATAL;
+ }
+ else // We can't stall. Read in the excess data and throw it away.
+ rc = throw_away_data(ums, bulk_ctxt);
+ }
+
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Medium ejection heuristics.
+ *
+ * Windows:
+ * Uses Start/Stop Unit. Only Stop with LoEj. Observed ONLY on very specific windows machines.
+ * Uses Prevent/Allow Medium Removal. (For big reads and ANY write.) //////Except trivial writes. Needs check with prefetch ON
+ * Sends Test Unit Ready every 1s at idle. (Needs 1 EP Timeout protection: 2s)
+ * Does not send data when ejects. In the case it does,
+ * it loops into Request Sense and Test Unit Ready when ejects.
+ * Line always at SE0 and only goes in J-State when it ejects.
+ *
+ * Linux:
+ * Uses Start/Stop Unit. Stops with LoEj when Media prevention is off.
+ * Uses Prevent/Allow Medium Removal. (For big read and any write.)
+ * Sends Test Unit Ready every 2s at idle. (Needs 2 EP Timeouts protection: 4s)
+ * Loops into Request Sense and Test Unit Ready when ejects.
+ * Line always at SE0.
+ *
+ * Mac OS:
+ * Uses Start/Stop. Stops with LoEj when Allow Medium Removal is enabled.
+ * Uses Prevent/Allow Medium Removal. (Properly. Enables at mount and only disables it when ejects.)
+ * Does not send Test Unit Ready at idle. But Prevent Medium Removal is enabled.
+ * Loops into Request Sense and Test Unit Ready when ejects.
+ * Line always at SE0.
+ */
+
+static int received_cbw(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ /* Was this a real packet? Should it be ignored? */
+ if (bulk_ctxt->bulk_out_status || bulk_ctxt->bulk_out_ignore || ums->lun.unmounted)
+ {
+ if (bulk_ctxt->bulk_out_status || ums->lun.unmounted)
+ {
+ DPRINTF("USB: EP timeout\n");
+ // In case we disconnected, exit UMS.
+ // Raise timeout if removable and didn't got a unit ready command inside 4s.
+ if (bulk_ctxt->bulk_out_status == USB2_ERROR_XFER_EP_DISABLED ||
+ (bulk_ctxt->bulk_out_status == USB_ERROR_TIMEOUT && ums->lun.removable && !ums->lun.prevent_medium_removal))
+ {
+ if (bulk_ctxt->bulk_out_status == USB_ERROR_TIMEOUT)
+ {
+ if (usb_ops.usb_device_get_port_in_sleep())
+ {
+ ums->set_text(ums->label, "#C7EA46 Status:# #FFFFFF EP in sleep#");
+ ums->timeouts += 14;
+ }
+ else if (!ums->xusb) // Timeout only on USB2.
+ {
+ ums->timeouts += 4;
+ DPRINTF("USB: EP removable\n");
+ }
+ }
+ else
+ {
+ gfx_printf("USB: EP disabled\n");
+ msleep(500);
+ ums->timeouts += 4;
+ }
+ }
+
+ if (ums->lun.unmounted)
+ {
+ ums->set_text(ums->label, "#C7EA46 Status:# #FFFFFF Medium unmounted#");
+ ums->timeouts++;
+ }
+
+ if (ums->timeouts > 20)
+ raise_exception(ums, UMS_STATE_EXIT);
+ }
+
+ if (bulk_ctxt->bulk_out_status || bulk_ctxt->bulk_out_ignore)
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Is the CBW valid? */
+ bulk_recv_pkt_t *cbw = (bulk_recv_pkt_t *)bulk_ctxt->bulk_out_buf;
+ if (bulk_ctxt->bulk_out_length_actual != USB_BULK_CB_WRAP_LEN || cbw->Signature != USB_BULK_CB_SIG)
+ {
+ gfx_printf("USB: invalid CBW: len %X sig 0x%X\n", bulk_ctxt->bulk_out_length_actual, cbw->Signature);
+
+ // The Bulk-only spec says we MUST stall the IN endpoint
+ // (6.6.1), so it's unavoidable. It also says we must
+ // retain this state until the next reset, but there's
+ // no way to tell the controller driver it should ignore
+ // Clear-Feature(HALT) requests.
+ //
+ // We aren't required to halt the OUT endpoint; instead
+ // we can simply accept and discard any data received
+ // until the next reset.
+ ums_wedge_bulk_in_endpoint(ums);
+ bulk_ctxt->bulk_out_ignore = 1;
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Is the CBW meaningful? */
+ if (cbw->Lun >= UMS_MAX_LUN || cbw->Flags & ~USB_BULK_IN_FLAG ||
+ cbw->Length <= 0 || cbw->Length > SCSI_MAX_CMD_SZ)
+ {
+ gfx_printf("USB: non-meaningful CBW: lun = %X, flags = 0x%X, cmdlen %X\n",
+ cbw->Lun, cbw->Flags, cbw->Length);
+
+ /* We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to. */
+ if (ums->can_stall)
+ {
+ ums_set_stall(bulk_ctxt->bulk_out);
+ ums_set_stall(bulk_ctxt->bulk_in);
+ ums->set_text(ums->label, "#C7EA46 Status:# #FFFFFF CBW unknown - Stalled both EP#");
+ }
+
+ return UMS_RES_INVALID_ARG;
+ }
+
+ /* Save the command for later */
+ ums->cmnd_size = cbw->Length;
+ memcpy(ums->cmnd, cbw->CDB, ums->cmnd_size);
+
+ if (cbw->Flags & USB_BULK_IN_FLAG)
+ ums->data_dir = DATA_DIR_TO_HOST;
+ else
+ ums->data_dir = DATA_DIR_FROM_HOST;
+
+ ums->data_size = cbw->DataTransferLength;
+
+ if (ums->data_size == 0)
+ ums->data_dir = DATA_DIR_NONE;
+
+ ums->lun_idx = cbw->Lun;
+ ums->tag = cbw->Tag;
+
+ if (!ums->lun.unmounted)
+ ums->timeouts = 0;
+
+ return UMS_RES_OK;
+}
+
+static int get_next_command(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ int rc = UMS_RES_OK;
+
+ /* Wait for the next buffer to become available */
+ // while (bulk_ctxt->bulk_out_buf_state != BUF_STATE_EMPTY)
+ // {
+ // //wait irq.
+ // }
+
+ bulk_ctxt->bulk_out_length = USB_BULK_CB_WRAP_LEN;
+
+ /* Queue a request to read a Bulk-only CBW */
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED_CMD);
+
+ /* We will drain the buffer in software, which means we
+ * can reuse it for the next filling. No need to advance
+ * next_buffhd_to_fill. */
+
+ /* Wait for the CBW to arrive */
+ // while (bulk_ctxt->bulk_out_buf_state != BUF_STATE_FULL)
+ // {
+ // //wait irq.
+ // }
+
+ rc = received_cbw(ums, bulk_ctxt);
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_EMPTY;
+
+ return rc;
+}
+
+static void send_status(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ u8 status = USB_STATUS_PASS;
+ u32 sd = ums->lun.sense_data;
+
+ if (ums->phase_error)
+ {
+ ums->set_text(ums->label, "#C7EA46 Error:# Phase-error!");
+ status = USB_STATUS_PHASE_ERROR;
+ sd = SS_INVALID_COMMAND;
+ }
+ else if (sd != SS_NO_SENSE)
+ {
+ DPRINTF("USB: CMD fail\n");
+ status = USB_STATUS_FAIL;
+ DPRINTF("USB: Sense: SK x%02X, ASC x%02X, ASCQ x%02X; info x%X\n",
+ SK(sd), ASC(sd), ASCQ(sd), ums->lun.sense_data_info);
+ }
+
+ /* Store and send the Bulk-only CSW */
+ bulk_send_pkt_t *csw = (bulk_send_pkt_t *)bulk_ctxt->bulk_in_buf;
+
+ csw->Signature = USB_BULK_CS_SIG;
+ csw->Tag = ums->tag;
+ csw->Residue = ums->residue;
+ csw->Status = status;
+
+ bulk_ctxt->bulk_in_length = USB_BULK_CS_WRAP_LEN;
+ _ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_CMD);
+}
+
+static void handle_exception(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
+{
+ enum ums_state old_state;
+
+ /* Clear out the controller's fifos */
+ ums_flush_endpoint(bulk_ctxt->bulk_in);
+ ums_flush_endpoint(bulk_ctxt->bulk_out);
+
+ /* Reset the I/O buffer states and pointers, the SCSI
+ * state, and the exception. Then invoke the handler. */
+
+ bulk_ctxt->bulk_in_buf_state = BUF_STATE_EMPTY;
+ bulk_ctxt->bulk_out_buf_state = BUF_STATE_EMPTY;
+
+ old_state = ums->state;
+
+ if (old_state != UMS_STATE_ABORT_BULK_OUT)
+ {
+ ums->lun.prevent_medium_removal = 0;
+ ums->lun.sense_data = SS_NO_SENSE;
+ ums->lun.unit_attention_data = SS_NO_SENSE;
+ ums->lun.sense_data_info = 0;
+ ums->lun.info_valid = 0;
+ }
+
+ ums->state = UMS_STATE_NORMAL;
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state)
+ {
+ case UMS_STATE_NORMAL:
+ break;
+ case UMS_STATE_ABORT_BULK_OUT:
+ send_status(ums, bulk_ctxt);
+ break;
+
+ case UMS_STATE_PROTOCOL_RESET:
+ /* In case we were forced against our will to halt a
+ * bulk endpoint, clear the halt now. (The SuperH UDC
+ * requires this.) */
+ if (bulk_ctxt->bulk_out_ignore)
+ {
+ bulk_ctxt->bulk_out_ignore = 0;
+ ums_clear_stall(bulk_ctxt->bulk_in);
+ }
+ ums->lun.unit_attention_data = SS_RESET_OCCURRED;
+ break;
+
+ case UMS_STATE_EXIT:
+ ums->state = UMS_STATE_TERMINATED; /* Stop the thread */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static inline void _system_maintainance(usbd_gadget_ums_t *ums)
+{
+ static u32 timer_dram = 0;
+ static u32 timer_status_bar = 0;
+
+ u32 time = get_tmr_ms();
+
+ if (timer_status_bar < time)
+ {
+ ums->system_maintenance(true);
+ timer_status_bar = get_tmr_ms() + 30000;
+ }
+ else if (timer_dram < time)
+ {
+ minerva_periodic_training();
+ timer_dram = get_tmr_ms() + EMC_PERIODIC_TRAIN_MS;
+ }
+}
+
+int usb_device_gadget_ums(usb_ctxt_t *usbs)
+{
+ int res = 0;
+ sdmmc_t sdmmc;
+ sdmmc_storage_t storage;
+ usbd_gadget_ums_t ums = {0};
+
+ // Get USB Controller ops.
+ if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210)
+ usb_device_get_ops(&usb_ops);
+ else
+ {
+ ums.xusb = true;
+ xusb_device_get_ops(&usb_ops);
+ }
+
+ usbs->set_text(usbs->label, "#C7EA46 Status:# #FFFFFF Started USB#");
+
+ if (usb_ops.usb_device_init())
+ {
+ usb_ops.usbd_end(false, true);
+ return 1;
+ }
+
+ ums.state = UMS_STATE_NORMAL;
+ ums.can_stall = 0;
+
+ ums.bulk_ctxt.bulk_in = USB_EP_BULK_IN;
+ ums.bulk_ctxt.bulk_in_buf = (u8 *)USB_EP_BULK_IN_BUF_ADDR;
+
+ ums.bulk_ctxt.bulk_out = USB_EP_BULK_OUT;
+ ums.bulk_ctxt.bulk_out_buf = (u8 *)USB_EP_BULK_OUT_BUF_ADDR;
+
+ // Set LUN parameters.
+ ums.lun.ro = usbs->ro;
+ ums.lun.type = usbs->type;
+ ums.lun.partition = usbs->partition;
+ ums.lun.offset = usbs->offset;
+ ums.lun.removable = 1; // Always removable to force OSes to use prevent media removal.
+ ums.lun.unit_attention_data = SS_RESET_OCCURRED;
+
+ // Set system functions
+ ums.label = usbs->label;
+ ums.set_text = usbs->set_text;
+ ums.system_maintenance = usbs->system_maintenance;
+
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Mounting disk#");
+
+ // Initialize sdmmc.
+ if (usbs->type == MMC_SD)
+ {
+ sd_mount();
+ sd_unmount();
+ ums.lun.sdmmc = &sd_sdmmc;
+ ums.lun.storage = &sd_storage;
+ }
+ else
+ {
+ ums.lun.sdmmc = &sdmmc;
+ ums.lun.storage = &storage;
+ sdmmc_storage_init_mmc(ums.lun.storage, ums.lun.sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400);
+ sdmmc_storage_set_mmc_partition(ums.lun.storage, ums.lun.partition - 1);
+ }
+
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Waiting for connection#");
+
+ // Initialize Control Endpoint.
+ if (usb_ops.usb_device_enumerate(USB_GADGET_UMS))
+ goto error;
+
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Waiting for LUN#");
+
+ if (usb_ops.usb_device_class_send_max_lun(0)) // One device for now.
+ goto error;
+
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Started UMS#");
+
+ if (usbs->sectors)
+ ums.lun.num_sectors = usbs->sectors;
+ else
+ ums.lun.num_sectors = ums.lun.storage->sec_cnt;
+
+ do
+ {
+ // Do DRAM training and update system tasks.
+ _system_maintainance(&ums);
+
+ // Check for force unmount button combo.
+ if (btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ {
+ // Check if we are allowed to unload the media.
+ if (ums.lun.prevent_medium_removal)
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Unload attempt prevented#");
+ else
+ break;
+ }
+
+ if (ums.state != UMS_STATE_NORMAL)
+ {
+ handle_exception(&ums, &ums.bulk_ctxt);
+ continue;
+ }
+
+ ums_handle_ep0_ctrl(&ums);
+
+ if (get_next_command(&ums, &ums.bulk_ctxt) || (ums.state > UMS_STATE_NORMAL))
+ continue;
+
+ ums_handle_ep0_ctrl(&ums);
+
+ if (_ums_parse_scsi_cmd(&ums, &ums.bulk_ctxt) || (ums.state > UMS_STATE_NORMAL))
+ continue;
+
+ ums_handle_ep0_ctrl(&ums);
+
+ if (finish_reply(&ums, &ums.bulk_ctxt) || (ums.state > UMS_STATE_NORMAL))
+ continue;
+
+ send_status(&ums, &ums.bulk_ctxt);
+ } while (ums.state != UMS_STATE_TERMINATED);
+
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Disk ejected#");
+ goto exit;
+
+error:
+ ums.set_text(ums.label, "#C7EA46 Status:# #FFFFFF Timed out or canceled#");
+ res = 1;
+
+exit:
+ if (ums.lun.type == MMC_EMMC)
+ sdmmc_storage_end(ums.lun.storage);
+
+ usb_ops.usbd_end(true, false);
+
+ return res;
+}
diff --git a/bdk/usb/usb_t210.h b/bdk/usb/usb_t210.h
new file mode 100644
index 00000000..e677a5f3
--- /dev/null
+++ b/bdk/usb/usb_t210.h
@@ -0,0 +1,293 @@
+/*
+ * Enhanced & eXtensible USB device (EDCI & XDCI) driver for Tegra X1
+ *
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _USB_T210_H_
+#define _USB_T210_H_
+
+#include
+
+/* EHCI USB */
+
+/* General USB registers */
+#define USB1_IF_USB_SUSP_CTRL 0x400
+#define SUSP_CTRL_USB_WAKE_ON_CNNT_EN_DEV BIT(3)
+#define SUSP_CTRL_USB_WAKE_ON_DISCON_EN_DEV BIT(4)
+#define SUSP_CTRL_USB_PHY_CLK_VALID BIT(7)
+#define SUSP_CTRL_UTMIP_RESET BIT(11)
+#define SUSP_CTRL_UTMIP_PHY_ENB BIT(12)
+#define SUSP_CTRL_UTMIP_UTMIP_SUSPL1_SET BIT(25)
+#define USB1_IF_USB_PHY_VBUS_SENSORS 0x404
+#define USB1_UTMIP_XCVR_CFG0 0x808
+#define USB1_UTMIP_BIAS_CFG0 0x80C
+#define USB1_UTMIP_HSRX_CFG0 0x810
+#define USB1_UTMIP_HSRX_CFG1 0x814
+#define USB1_UTMIP_TX_CFG0 0x820
+#define USB1_UTMIP_MISC_CFG1 0x828
+#define USB1_UTMIP_DEBOUNCE_CFG0 0x82C
+#define USB1_UTMIP_BAT_CHRG_CFG0 0x830
+#define BAT_CHRG_CFG0_PWRDOWN_CHRG BIT(0)
+#define BAT_CHRG_CFG0_OP_SRC_EN BIT(3)
+#define USB1_UTMIP_SPARE_CFG0 0x834
+#define USB1_UTMIP_XCVR_CFG1 0x838
+#define USB1_UTMIP_BIAS_CFG1 0x83C
+#define USB1_UTMIP_BIAS_CFG2 0x850
+#define USB1_UTMIP_XCVR_CFG2 0x854
+#define USB1_UTMIP_XCVR_CFG3 0x858
+
+/* USB Queue Head Descriptor */
+#define USB2_QH_USB2D_QH_EP_BASE (USB_BASE + 0x1000)
+#define USB_QHD_EP_CAP_IOS_ENABLE BIT(15)
+#define USB_QHD_EP_CAP_MAX_PKT_LEN_MASK 0x7FF
+#define USB_QHD_EP_CAP_ZERO_LEN_TERM_DIS BIT(29)
+#define USB_QHD_EP_CAP_MULTI_NON_ISO (0 << 30)
+#define USB_QHD_EP_CAP_MULTI_1 (1 << 30)
+#define USB_QHD_EP_CAP_MULTI_2 (2 << 30)
+#define USB_QHD_EP_CAP_MULTI_3 (3 << 30)
+
+#define USB_QHD_TOKEN_XFER_ERROR BIT(3)
+#define USB_QHD_TOKEN_BUFFER_ERROR BIT(5)
+#define USB_QHD_TOKEN_HALTED BIT(6)
+#define USB_QHD_TOKEN_ACTIVE BIT(7)
+#define USB_QHD_TOKEN_MULT_OVERR_MASK (2 << 10)
+#define USB_QHD_TOKEN_IRQ_ON_COMPLETE BIT(15)
+#define USB_QHD_TOKEN_TOTAL_BYTES_SHIFT 16
+
+/* USB_OTG/USB_1 controllers register bits */
+#define USB2D_PORTSC1_SUSP BIT(7)
+
+#define USB2D_USBCMD_RUN BIT(0)
+#define USB2D_USBCMD_RESET BIT(1)
+#define USB2D_USBCMD_ITC_MASK (0xFF << 16)
+
+#define USB2D_USBSTS_UI BIT(0)
+#define USB2D_USBSTS_UEI BIT(1)
+#define USB2D_USBSTS_PCI BIT(2)
+#define USB2D_USBSTS_FRI BIT(3)
+#define USB2D_USBSTS_SEI BIT(4)
+#define USB2D_USBSTS_AAI BIT(5)
+#define USB2D_USBSTS_URI BIT(6)
+#define USB2D_USBSTS_SRI BIT(7)
+#define USB2D_USBSTS_SLI BIT(8)
+
+#define USB2D_USBMODE_CM_MASK (3 << 0)
+#define USB2D_USBMODE_CM_IDLE 0
+#define USB2D_USBMODE_CM_RSVD 1
+#define USB2D_USBMODE_CM_DEVICE 2
+#define USB2D_USBMODE_CM_HOST 3
+
+#define USB2D_ENDPT_STATUS_RX_OFFSET BIT(0)
+#define USB2D_ENDPT_STATUS_TX_OFFSET BIT(16)
+
+#define USB2D_ENDPTCTRL_RX_EP_STALL BIT(0)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_CTRL (0 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_ISO (1 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_BULK (2 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_INTR (3 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_MASK (3 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_INHIBIT BIT(5)
+#define USB2D_ENDPTCTRL_RX_EP_RESET BIT(6)
+#define USB2D_ENDPTCTRL_RX_EP_ENABLE BIT(7)
+#define USB2D_ENDPTCTRL_TX_EP_STALL BIT(16)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_CTRL (0 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_ISO (1 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_BULK (2 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_INTR (3 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_MASK (3 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_INHIBIT BIT(21)
+#define USB2D_ENDPTCTRL_TX_EP_RESET BIT(22)
+#define USB2D_ENDPTCTRL_TX_EP_ENABLE BIT(23)
+
+#define USB2D_HOSTPC1_DEVLC_ASUS BIT(17)
+#define USB2D_HOSTPC1_DEVLC_PHCD BIT(22)
+#define USB2D_HOSTPC1_DEVLC_PSPD_MASK (3 << 25)
+
+#define USB2D_OTGSC_USB_ID_PULLUP BIT(5)
+#define USB2D_OTGSC_USB_IRQ_STS_MASK (0x7F << 16)
+
+/* USB_OTG/USB_1 controllers registers */
+typedef struct _t210_usb2d_t
+{
+ vu32 id;
+ vu32 unk0;
+ vu32 hw_host;
+ vu32 hw_device;
+ vu32 hw_txbuf;
+ vu32 hw_rxbuf;
+ vu32 unk1[26];
+ vu32 gptimer0ld;
+ vu32 gptimer0ctrl;
+ vu32 gptimer1ld;
+ vu32 gptimer1ctrl;
+ vu32 unk2[28];
+ vu16 caplength;
+ vu16 hciversion;
+ vu32 hcsparams;
+ vu32 hccparams;
+ vu32 unk3[5];
+ vu32 dciversion;
+ vu32 dccparams;
+ vu32 extsts;
+ vu32 usbextintr;
+ vu32 usbcmd;
+ vu32 usbsts;
+ vu32 usbintr;
+ vu32 frindex;
+ vu32 unk4;
+ vu32 periodiclistbase;
+ vu32 asynclistaddr;
+ vu32 asyncttsts;
+ vu32 burstsize;
+ vu32 txfilltuning;
+ vu32 unk6;
+ vu32 icusb_ctrl;
+ vu32 ulpi_viewport;
+ vu32 rsvd0[4];
+ vu32 portsc1;
+ vu32 rsvd1[15];
+ vu32 hostpc1_devlc;
+ vu32 rsvd2[15];
+ vu32 otgsc;
+ vu32 usbmode;
+ vu32 unk10;
+ vu32 endptnak;
+ vu32 endptnak_enable;
+ vu32 endptsetupstat;
+ vu32 endptprime;
+ vu32 endptflush;
+ vu32 endptstatus;
+ vu32 endptcomplete;
+ vu32 endptctrl[16];
+} t210_usb2d_t;
+
+
+/* XHCI USB */
+
+/* XUSB DEV XHCI registers */
+#define XUSB_DEV_XHCI_DB 0x4
+#define XUSB_DEV_XHCI_ERSTSZ 0x8
+#define XUSB_DEV_XHCI_ERST0BALO 0x10
+#define XUSB_DEV_XHCI_ERST0BAHI 0x14
+#define XUSB_DEV_XHCI_ERST1BALO 0x18
+#define XUSB_DEV_XHCI_ERST1BAHI 0x1C
+#define XUSB_DEV_XHCI_ERDPLO 0x20
+#define XHCI_ERDPLO_EHB BIT(3)
+#define XUSB_DEV_XHCI_ERDPHI 0x24
+#define XUSB_DEV_XHCI_EREPLO 0x28
+#define XCHI_ECS BIT(0)
+#define XUSB_DEV_XHCI_EREPHI 0x2C
+#define XUSB_DEV_XHCI_CTRL 0x30
+#define XHCI_CTRL_RUN BIT(0)
+#define XHCI_CTRL_LSE BIT(1)
+#define XHCI_CTRL_IE BIT(4)
+#define XHCI_CTRL_ENABLE BIT(31)
+#define XUSB_DEV_XHCI_ST 0x34
+#define XHCI_ST_RC BIT(0)
+#define XHCI_ST_IP BIT(4)
+#define XUSB_DEV_XHCI_RT_IMOD 0x38
+#define XUSB_DEV_XHCI_PORTSC 0x3C
+#define XHCI_PORTSC_PR BIT(4)
+#define XHCI_PORTSC_PLS_MASK (0xF << 5)
+#define XHCI_PORTSC_PLS_U0 (0 << 5)
+#define XHCI_PORTSC_PLS_U1 (1 << 5)
+#define XHCI_PORTSC_PLS_U2 (2 << 5)
+#define XHCI_PORTSC_PLS_U3 (3 << 5)
+#define XHCI_PORTSC_PLS_DISABLED (4 << 5)
+#define XHCI_PORTSC_PLS_RXDETECT (5 << 5)
+#define XHCI_PORTSC_PLS_INACTIVE (6 << 5)
+#define XHCI_PORTSC_PLS_POLLING (7 << 5)
+#define XHCI_PORTSC_PLS_RECOVERY (8 << 5)
+#define XHCI_PORTSC_PLS_HOTRESET (9 << 5)
+#define XHCI_PORTSC_PLS_COMPLIANCE (10 << 5)
+#define XHCI_PORTSC_PLS_LOOPBACK (11 << 5)
+#define XHCI_PORTSC_PLS_RESUME (15 << 5)
+#define XHCI_PORTSC_PS (0xF << 10)
+#define XHCI_PORTSC_LWS BIT(16)
+#define XHCI_PORTSC_CSC BIT(17)
+#define XHCI_PORTSC_WRC BIT(19)
+#define XHCI_PORTSC_PRC BIT(21)
+#define XHCI_PORTSC_PLC BIT(22)
+#define XHCI_PORTSC_CEC BIT(23)
+#define XHCI_PORTSC_WPR BIT(30)
+#define XUSB_DEV_XHCI_ECPLO 0x40
+#define XUSB_DEV_XHCI_ECPHI 0x44
+#define XUSB_DEV_XHCI_EP_HALT 0x50
+#define XHCI_EP_HALT_DCI BIT(0)
+#define XUSB_DEV_XHCI_EP_PAUSE 0x54
+#define XUSB_DEV_XHCI_EP_RELOAD 0x58
+#define XUSB_DEV_XHCI_EP_STCHG 0x5C
+#define XUSB_DEV_XHCI_PORTHALT 0x6C
+#define XHCI_PORTHALT_HALT_LTSSM BIT(0)
+#define XHCI_PORTHALT_STCHG_REQ BIT(20)
+#define XUSB_DEV_XHCI_CFG_DEV_FE 0x85C
+#define XHCI_CFG_DEV_FE_PORTREGSEL_MASK (3 << 0)
+#define XHCI_CFG_DEV_FE_PORTREGSEL_SS (1 << 0)
+#define XHCI_CFG_DEV_FE_PORTREGSEL_HSFS (2 << 0)
+
+/* XUSB DEV PCI registers */
+#define XUSB_CFG_1 0x4
+#define CFG_1_IO_SPACE BIT(0)
+#define CFG_1_MEMORY_SPACE BIT(1)
+#define CFG_1_BUS_MASTER BIT(2)
+#define XUSB_CFG_4 0x10
+#define CFG_4_ADDRESS_TYPE_32_BIT (0 << 1)
+#define CFG_4_ADDRESS_TYPE_64_BIT (2 << 1)
+
+/* XUSB DEV Device registers */
+#define XUSB_DEV_CONFIGURATION 0x180
+#define DEV_CONFIGURATION_EN_FPCI BIT(0)
+#define XUSB_DEV_INTR_MASK 0x188
+#define DEV_INTR_MASK_IP_INT_MASK BIT(16)
+
+/* XUSB Pad Control registers */
+#define XUSB_PADCTL_USB2_PAD_MUX 0x4
+#define PADCTL_USB2_PAD_MUX_USB2_OTG_PAD_PORT0_USB2 (0 << 0)
+#define PADCTL_USB2_PAD_MUX_USB2_OTG_PAD_PORT0_XUSB (1 << 0)
+#define PADCTL_USB2_PAD_MUX_USB2_OTG_PAD_PORT0_MASK (3 << 0)
+#define PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_USB2 (0 << 18)
+#define PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB (1 << 18)
+#define PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK (3 << 18)
+#define XUSB_PADCTL_USB2_PORT_CAP 0x8
+#define PADCTL_USB2_PORT_CAP_PORT_0_CAP_DIS (0 << 0)
+#define PADCTL_USB2_PORT_CAP_PORT_0_CAP_HOST (1 << 0)
+#define PADCTL_USB2_PORT_CAP_PORT_0_CAP_DEV (2 << 0)
+#define PADCTL_USB2_PORT_CAP_PORT_0_CAP_OTG (3 << 0)
+#define PADCTL_USB2_PORT_CAP_PORT_0_CAP_MASK (3 << 0)
+#define XUSB_PADCTL_SS_PORT_MAP 0x14
+#define PADCTL_SS_PORT_MAP_PORT0_MASK (0xF << 0)
+#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20
+#define XUSB_PADCTL_ELPG_PROGRAM_1 0x24
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0 0x80
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1 0x84
+#define XUSB_PADCTL_USB2_OTG_PAD0_CTL_0 0x88
+#define XUSB_PADCTL_USB2_OTG_PAD0_CTL_1 0x8C
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0 0x284
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_1 0x288
+#define XUSB_PADCTL_USB2_VBUS_ID 0xC60
+#define PADCTL_USB2_VBUS_ID_VBUS_OVR_EN (1 << 12)
+#define PADCTL_USB2_VBUS_ID_VBUS_OVR_MASK (3 << 12)
+#define PADCTL_USB2_VBUS_ID_VBUS_ON BIT(14)
+#define PADCTL_USB2_VBUS_ID_SRC_ID_OVR_EN (1 << 16)
+#define PADCTL_USB2_VBUS_ID_SRC_MASK (3 << 16)
+#define PADCTL_USB2_VBUS_ID_OVR_GND (0 << 18)
+#define PADCTL_USB2_VBUS_ID_OVR_C (1 << 18)
+#define PADCTL_USB2_VBUS_ID_OVR_B (2 << 18)
+#define PADCTL_USB2_VBUS_ID_OVR_A (4 << 18)
+#define PADCTL_USB2_VBUS_ID_OVR_FLOAT (8 << 18)
+#define PADCTL_USB2_VBUS_ID_OVR_MASK (0xF << 18)
+
+#endif
diff --git a/bdk/usb/usbd.c b/bdk/usb/usbd.c
new file mode 100644
index 00000000..95d1ed5c
--- /dev/null
+++ b/bdk/usb/usbd.c
@@ -0,0 +1,1595 @@
+/*
+ * Enhanced USB Device (EDCI) driver for Tegra X1
+ *
+ * Copyright (c) 2019-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+typedef enum
+{
+ USB_HW_EP0 = 0,
+ USB_HW_EP1 = 1
+} usb_hw_ep_t;
+
+typedef enum
+{
+ USB_EP_STATUS_IDLE = 0,
+ USB_EP_STATUS_ACTIVE = 1,
+ USB_EP_STATUS_ERROR = 2,
+ USB_EP_STATUS_NO_CONFIG = 3,
+ USB_EP_STATUS_STALLED = 4,
+ USB_EP_STATUS_DISABLED = 5
+} usb_ep_status_t;
+
+typedef enum {
+ USB_LOW_SPEED = 0,
+ USB_FULL_SPEED = 1,
+ USB_HIGH_SPEED = 2,
+ USB_SUPER_SPEED = 3,
+} usb_speed_t;
+
+typedef struct _dTD_t
+{
+ vu32 next_dTD;
+ vu32 info;
+ vu32 pages[5];
+ vu32 reserved;
+} dTD_t;
+
+typedef struct _dQH_t
+{
+ vu32 ep_capabilities;
+ vu32 curr_dTD_ptr;
+ vu32 next_dTD_ptr;
+ vu32 token;
+ vu32 buffers[5]; // hmmm.
+ vu32 reserved;
+ vu32 setup[2];
+ vu32 gap[4];
+} dQH_t;
+
+typedef struct _usbd_t
+{
+ volatile dTD_t dtds[4 * 4]; // 4 dTD per endpoint.
+ volatile dQH_t *qhs;
+ int ep_configured[4];
+ int ep_bytes_requested[4];
+} usbd_t;
+
+typedef struct _usbd_controller_t
+{
+ u32 port_speed;
+ t210_usb2d_t *regs;
+ usb_ctrl_setup_t control_setup;
+ usb_desc_t *desc;
+ usb_gadget_type gadget;
+ u8 config_num;
+ u8 interface_num;
+ u8 max_lun;
+ bool usb_phy_ready;
+ bool configuration_set;
+ bool max_lun_set;
+ bool bulk_reset_req;
+ bool hid_report_sent;
+ u32 charger_detect;
+} usbd_controller_t;
+
+extern u8 hid_report_descriptor_jc[];
+extern u8 hid_report_descriptor_touch[];
+extern u32 hid_report_descriptor_jc_size;
+extern u32 hid_report_descriptor_touch_size;
+
+extern usb_desc_t usb_gadget_hid_jc_descriptors;
+extern usb_desc_t usb_gadget_hid_touch_descriptors;
+extern usb_desc_t usb_gadget_ums_descriptors;
+
+usbd_t *usbdaemon;
+
+usbd_controller_t *usbd_otg;
+usbd_controller_t usbd_usb_otg_controller_ctxt;
+
+bool usb_init_done = false;
+
+u8 *usb_ep0_ctrl_buf = (u8 *)USB_EP_CONTROL_BUF_ADDR;
+
+static int _usbd_reset_usb_otg_phy_device_mode()
+{
+ usbd_otg->usb_phy_ready = false;
+
+ // Clear UTMIP reset.
+ USB(USB1_IF_USB_SUSP_CTRL) &= ~SUSP_CTRL_UTMIP_RESET;
+
+ // Wait for PHY clock to get validated.
+ u32 retries = 100000; // 200ms timeout.
+ while (!(USB(USB1_IF_USB_SUSP_CTRL) & SUSP_CTRL_USB_PHY_CLK_VALID))
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_INIT;
+ usleep(1);
+ }
+ usbd_otg->usb_phy_ready = true;
+
+ // Clear all device addresses, enabled setup requests and transmit events.
+ usbd_otg->regs->periodiclistbase = 0;
+ usbd_otg->regs->endptsetupstat = usbd_otg->regs->endptsetupstat;
+ usbd_otg->regs->endptcomplete = usbd_otg->regs->endptcomplete;
+
+ // Stop device controller.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_RUN;
+
+ // Set controller mode to idle.
+ usbd_otg->regs->usbmode &= ~USB2D_USBMODE_CM_MASK;
+
+ // Reset the controller.
+ usbd_otg->regs->usbcmd |= USB2D_USBCMD_RESET;
+
+ // Wait for the reset to complete.
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->usbcmd & USB2D_USBCMD_RESET)
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_INIT;
+ usleep(1);
+ }
+
+ // Wait for PHY clock to get validated after reset.
+ retries = 100000; // 200ms timeout.
+ while (!(USB(USB1_IF_USB_SUSP_CTRL) & SUSP_CTRL_USB_PHY_CLK_VALID))
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_INIT;
+ usleep(1);
+ }
+
+ // Set controller to Device mode.
+ usbd_otg->regs->usbmode = (usbd_otg->regs->usbmode & ~USB2D_USBMODE_CM_MASK) | USB2D_USBMODE_CM_DEVICE;
+
+ // Wait for the selected mode to be enabled.
+ retries = 100000; // 200ms timeout.
+ while ((usbd_otg->regs->usbmode & USB2D_USBMODE_CM_MASK) != USB2D_USBMODE_CM_DEVICE)
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_INIT;
+ usleep(1);
+ }
+
+ // Disable all interrupts.
+ usbd_otg->regs->usbintr = 0;
+
+ // Set the ID pullup and disable all OTGSC interrupts.
+ usbd_otg->regs->otgsc = USB2D_OTGSC_USB_ID_PULLUP;
+
+ // Clear all relevant interrupt statuses.
+ usbd_otg->regs->usbsts =
+ USB2D_USBSTS_UI | USB2D_USBSTS_UEI | USB2D_USBSTS_PCI |
+ USB2D_USBSTS_FRI | USB2D_USBSTS_SEI | USB2D_USBSTS_AAI |
+ USB2D_USBSTS_URI | USB2D_USBSTS_SRI | USB2D_USBSTS_SLI;
+
+ // Disable and clear all OTGSC interrupts.
+ usbd_otg->regs->otgsc = USB2D_OTGSC_USB_IRQ_STS_MASK;
+
+ // Clear EP0, EP1, EP2 setup requests.
+ usbd_otg->regs->endptsetupstat = 7; //TODO: Shouldn't this be endptsetupstat = endptsetupstat?
+
+ // Set all interrupts to immediate.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_ITC_MASK;
+
+ return USB_RES_OK;
+}
+
+static void _usb_charger_detect()
+{
+ // Charger detect init.
+ usbd_otg->charger_detect = 0;
+ bool charger_detect_enable = FUSE(FUSE_RESERVED_SW) & 0x10; // Disabled on Switch production.
+ if (charger_detect_enable)
+ {
+ usbd_otg->charger_detect |= 1;
+ // Configure detect pin.
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) &= ~(PINMUX_PARKED | PINMUX_TRISTATE | PINMUX_PULL_MASK);
+ gpio_config(GPIO_PORT_V, GPIO_PIN_3, GPIO_MODE_GPIO);
+
+ // Configure charger pin.
+ PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN1) &=
+ ~(PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_TRISTATE | PINMUX_PULL_MASK);
+ gpio_config(GPIO_PORT_CC, GPIO_PIN_5, GPIO_MODE_GPIO);
+ gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_5, GPIO_OUTPUT_ENABLE);
+
+ // Enable charger.
+ if (gpio_read(GPIO_PORT_V, GPIO_PIN_3))
+ {
+ usbd_otg->charger_detect |= 2;
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
+ usbd_otg->charger_detect |= 0x100;
+ USB(USB1_UTMIP_BAT_CHRG_CFG0) = BAT_CHRG_CFG0_OP_SRC_EN; // Clears UTMIP_PD_CHRG and enables charger detect.
+ usleep(5000);
+ }
+ }
+}
+
+static void _usb_init_phy()
+{
+ // Configure and enable PLLU.
+ clock_enable_pllu();
+
+ // Enable USBD clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_USBD);
+ usleep(2);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_USBD);
+ usleep(2);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_USBD);
+ usleep(2);
+
+ // Clear XUSB_PADCTL reset
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_CLR) = BIT(CLK_W_XUSB_PADCTL);
+
+ // Enable USB PHY and reset for programming.
+ u32 usb_susp_ctrl = USB(USB1_IF_USB_SUSP_CTRL);
+ USB(USB1_IF_USB_SUSP_CTRL) = usb_susp_ctrl | SUSP_CTRL_UTMIP_RESET;
+ USB(USB1_IF_USB_SUSP_CTRL) = usb_susp_ctrl | SUSP_CTRL_UTMIP_PHY_ENB | SUSP_CTRL_UTMIP_RESET;
+
+ // Enable IDDQ control by software and disable UTMIPLL IDDQ.
+ CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) = (CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) & 0xFFFFFFFC) | 1;
+ usleep(10);
+
+ // Disable crystal clock.
+ USB(USB1_UTMIP_MISC_CFG1) &= 0xBFFFFFFF;
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) &= 0xBFFFFFFF;
+
+ // Set B_SESS_VLD.
+ USB(USB1_IF_USB_PHY_VBUS_SENSORS) |= 0x1000;
+ USB(USB1_IF_USB_PHY_VBUS_SENSORS) |= 0x800;
+
+ // Set UTMIPLL dividers and config based on OSC and enable it to 960 MHz.
+ clock_enable_utmipll();
+
+ // Configure UTMIP Transceiver Cells.
+ u32 fuse_usb_calib = FUSE(FUSE_USB_CALIB);
+ USB(USB1_UTMIP_XCVR_CFG0) = (((USB(USB1_UTMIP_XCVR_CFG0) & 0xFFFFFFF0) | (fuse_usb_calib & 0xF)) & 0xFE3FFFFF) | ((fuse_usb_calib & 0x3F) << 25 >> 29 << 22);
+ USB(USB1_UTMIP_XCVR_CFG1) = (USB(USB1_UTMIP_XCVR_CFG1) & 0xFFC3FFFF) | ((fuse_usb_calib << 21) >> 28 << 18);
+ USB(USB1_UTMIP_XCVR_CFG3) = (USB(USB1_UTMIP_XCVR_CFG3) & 0xFFFFC1FF) | ((FUSE(FUSE_USB_CALIB_EXT) & 0x1F) << 9);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFDFFFFF;
+ USB(USB1_UTMIP_XCVR_CFG2) = (USB(USB1_UTMIP_XCVR_CFG2) & 0xFFFFF1FF) | 0x400;
+ usleep(1);
+
+ // Configure misc UTMIP.
+ USB(USB1_UTMIP_DEBOUNCE_CFG0) = (USB(USB1_UTMIP_DEBOUNCE_CFG0) & 0xFFFF0000) | 0xBB80;
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFC0FF) | 0x100; // when osc is 38.4KHz
+
+ //USB(USB1_UTMIP_SPARE_CFG0) &= 0xFFFFFEE7; unpatched0
+ USB(USB1_UTMIP_BIAS_CFG2) |= 2; //patched0 - UTMIP_HSSQUELCH_LEVEL_NEW: 2.
+ USB(USB1_UTMIP_SPARE_CFG0) &= 0xFFFFFE67; //patched0 - FUSE_HS_IREF_CAP_CFG
+ USB(USB1_UTMIP_TX_CFG0) |= 0x80000;
+
+ //USB(USB1_UTMIP_HSRX_CFG0) = (USB(USB1_UTMIP_HSRX_CFG0) & 0xFFF003FF) | 0x88000 | 0x4000; unpatched1
+ USB(USB1_UTMIP_HSRX_CFG0) = (USB(USB1_UTMIP_HSRX_CFG0) & 0xF0F003FF) | 0x88000 | 0x4000; //patched1 - reset UTMIP_PCOUNT_UPDN_DIV: From 1 to 0.
+ USB(USB1_UTMIP_BIAS_CFG2) &= 0xFFFFFFF8; //patched1 - UTMIP_HSSQUELCH_LEVEL_NEW: 0
+
+ USB(USB1_UTMIP_HSRX_CFG1) = (USB(USB1_UTMIP_HSRX_CFG1) & 0xFFFFFFC1) | 0x12;
+ USB(USB1_UTMIP_MISC_CFG1) |= 0x40000000;
+
+ // Enable crystal clock.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) |= 0x40000000;
+
+ // Enable USB2 tracking clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_Y_SET) = BIT(CLK_Y_USB2_TRK);
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) & 0xFFFFFF00) | 6; // Set trank divisor to 4.
+
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFC03F07) | 0x78000 | 0x50; // Set delays.
+ USB(USB1_UTMIP_BIAS_CFG0) &= 0xFFFFFBFF; // Disable Power down bias circuit.
+ usleep(1);
+
+ // Force PDTRK input into power up.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFFFFE) | 2;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFF7FFFFF) | 1;
+ usleep(3);
+
+ // Force PDTRK input into power up.
+ USB(USB1_UTMIP_BIAS_CFG1) = USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFFFFE;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFF7FFFFF) | 1;
+
+ // Disable USB2 tracking clock and configure UTMIP misc.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_Y_CLR) = BIT(CLK_Y_USB2_TRK);
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) & 0xFEFFFFEA) | 0x2000000 | 0x28 | 2;
+ usleep(1);
+
+ USB(USB1_UTMIP_BIAS_CFG0) &= 0xFF3FF7FF;
+ usleep(1);
+
+ // Clear power downs on UTMIP ID and VBUS wake up, PD, PD2, PDZI, PDCHRP, PDDR.
+ PMC(APBDEV_PMC_USB_AO) &= 0xFFFFFFF3; // UTMIP ID and VBUS wake up.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFFBFFF; // UTMIP_FORCE_PD_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFEFFFF; // UTMIP_FORCE_PD2_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFBFFFF; // UTMIP_FORCE_PDZI_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG1) &= 0xFFFFFFFB; // UTMIP_FORCE_PDCHRP_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG1) &= 0xFFFFFFEF; // UTMIP_FORCE_PDDR_POWERDOWN.
+ usleep(1);
+}
+
+int usb_device_init()
+{
+ if (usb_init_done)
+ return USB_RES_OK;
+
+ // Initialize USB2 controller PHY.
+ _usb_init_phy();
+
+ // AHB USB performance cfg.
+ AHB_GIZMO(AHB_GIZMO_AHB_MEM) |= AHB_MEM_DONT_SPLIT_AHB_WR | AHB_MEM_ENB_FAST_REARBITRATE;
+ AHB_GIZMO(AHB_GIZMO_USB) |= AHB_GIZMO_IMMEDIATE;
+ AHB_GIZMO(AHB_ARBITRATION_PRIORITY_CTRL) = PRIORITY_CTRL_WEIGHT(7) | PRIORITY_SELECT_USB;
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) =
+ MEM_PREFETCH_ENABLE | MEM_PREFETCH_USB_MST_ID | MEM_PREFETCH_ADDR_BNDRY(12) | 0x1000; // Addr boundary 64KB, Inactivity 4096 cycles.
+
+ // Set software and hardware context storage and clear it.
+ usbdaemon = (usbd_t *)USBD_ADDR; // Depends on USB_TD_BUFFER_PAGE_SIZE aligned address.
+ usbd_otg = &usbd_usb_otg_controller_ctxt;
+ memset(usbd_otg, 0, sizeof(usbd_controller_t));
+ memset(usbdaemon, 0, sizeof(usbd_t));
+
+ usbd_otg->regs = (t210_usb2d_t *)USB_OTG_BASE;
+ usbd_otg->usb_phy_ready = false;
+
+ // Initialize USB PHY on the USB_OTG Controller (#1) in Device mode.
+ int res = _usbd_reset_usb_otg_phy_device_mode();
+ usbd_otg->configuration_set = false;
+
+ _usb_charger_detect();
+
+ if (!res)
+ usb_init_done = true;
+
+ return res;
+}
+
+static void _usb_device_power_down()
+{
+ // Enable PHY low power suspend.
+ usbd_otg->regs->hostpc1_devlc |= USB2D_HOSTPC1_DEVLC_PHCD;
+ // Do not use any controller regs after the above!
+ // A reset or clear of the PHCD suspend bit must happen.
+
+ // Power down OTG and Bias circuits.
+ USB(USB1_UTMIP_BIAS_CFG0) |= BIT(11) | BIT(10); // UTMIP_OTGPD, UTMIP_BIASPD.
+
+ // Power down ID detectors.
+ USB(USB1_UTMIP_BIAS_CFG0) |= BIT(23) | BIT(22); // UTMIP_IDPD_SEL, UTMIP_IDPD_VAL.
+
+ if (usbd_otg->charger_detect)
+ {
+ USB(USB1_UTMIP_BAT_CHRG_CFG0) = 1; //UTMIP_PD_CHRG
+ usbd_otg->charger_detect = 0;
+ }
+
+ // Power down the UTMIP transceivers.
+ // UTMIP_FORCE_PDZI_POWERDOWN, UTMIP_FORCE_PD2_POWERDOWN, UTMIP_FORCE_PD_POWERDOWN.
+ USB(USB1_UTMIP_XCVR_CFG0) |= BIT(18) | BIT(16) |BIT(14);
+ // UTMIP_FORCE_PDDR_POWERDOWN, UTMIP_FORCE_PDCHRP_POWERDOWN, UTMIP_FORCE_PDDISC_POWERDOWN.
+ USB(USB1_UTMIP_XCVR_CFG1) |= BIT(4) | BIT(2) | BIT(0);
+
+ // Keep UTMIP in reset.
+ USB(USB1_IF_USB_SUSP_CTRL) |= SUSP_CTRL_UTMIP_RESET;
+
+ // Power down PD trunk.
+ USB(USB1_UTMIP_BIAS_CFG1) |= BIT(0); //UTMIP_FORCE_PDTRK_POWERDOWN.
+
+ // Force UTMIP_PLL power down.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= BIT(14); // UTMIP_FORCE_PLL_ENABLE_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= BIT(12); // UTMIP_FORCE_PLL_ACTIVE_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) |= BIT(4) | BIT(0); // UTMIP_FORCE_PD_SAMP_A/C_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= BIT(16); // UTMIP_FORCE_PLLU_POWERDOWN.
+
+ // Disable crystal clock.
+ USB(USB1_UTMIP_MISC_CFG1) &= 0xBFFFFFFF;
+
+ // Force enable UTMIPLL IDDQ.
+ CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) |= 3;
+
+ // Set XUSB_PADCTL reset
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB_PADCTL);
+
+ // Disable USBD clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_USBD);
+
+ // Disable PLLU.
+ clock_disable_pllu();
+
+ usb_init_done = false;
+}
+
+static void _usbd_stall_reset_ep1(usb_dir_t direction, usb_ep_cfg_t stall)
+{
+ stall &= 1;
+ if (direction == USB_DIR_IN)
+ {
+ usbd_otg->regs->endptctrl[1] = (usbd_otg->regs->endptctrl[1] & ~USB2D_ENDPTCTRL_TX_EP_STALL) | ((u32)stall << 16);
+ if (!stall)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_TX_EP_RESET;
+ }
+ else
+ {
+ usbd_otg->regs->endptctrl[1] = (usbd_otg->regs->endptctrl[1] & ~USB2D_ENDPTCTRL_RX_EP_STALL) | stall;
+ if (!stall)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_RX_EP_RESET;
+ }
+}
+
+void usb_device_stall_ep1_bulk_out()
+{
+ _usbd_stall_reset_ep1(USB_DIR_OUT, USB_EP_CFG_STALL);
+}
+
+void usb_device_stall_ep1_bulk_in()
+{
+ _usbd_stall_reset_ep1(USB_DIR_IN, USB_EP_CFG_STALL);
+}
+
+static int _usbd_get_max_pkt_length(int endpoint)
+{
+ switch (endpoint)
+ {
+ case USB_EP_CTRL_OUT:
+ case USB_EP_CTRL_IN:
+ return 64;
+ case USB_EP_BULK_OUT:
+ case USB_EP_BULK_IN:
+ if (usbd_otg->port_speed == USB_HIGH_SPEED)
+ return 512;
+ else
+ return 64;
+ default:
+ return 64;
+ }
+}
+
+static void _usbd_initialize_ep_ctrl(u32 endpoint)
+{
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+
+ memset((void *)&usbdaemon->qhs[endpoint], 0, sizeof(dQH_t));
+
+ if (!endpoint)
+ usbdaemon->qhs[endpoint].ep_capabilities = USB_QHD_EP_CAP_IOS_ENABLE;
+
+ usbdaemon->qhs[endpoint].next_dTD_ptr = 1; // TERMINATE_SET
+
+ u32 max_packet_len = _usbd_get_max_pkt_length(endpoint) & USB_QHD_EP_CAP_MAX_PKT_LEN_MASK;
+ usbdaemon->qhs[endpoint].ep_capabilities |= max_packet_len << 16;
+
+ if (direction == USB_DIR_IN)
+ {
+ u32 endpoint_type = usbd_otg->regs->endptctrl[actual_ep] & ~USB2D_ENDPTCTRL_TX_EP_TYPE_MASK;
+ if (actual_ep)
+ endpoint_type |= usbd_otg->gadget ? USB2D_ENDPTCTRL_TX_EP_TYPE_INTR : USB2D_ENDPTCTRL_TX_EP_TYPE_BULK;
+ else
+ endpoint_type |= USB2D_ENDPTCTRL_TX_EP_TYPE_CTRL;
+
+ usbd_otg->regs->endptctrl[actual_ep] = endpoint_type;
+
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_TX_EP_STALL;
+
+ if (actual_ep == USB_HW_EP1)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_TX_EP_RESET;
+
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_TX_EP_ENABLE;
+ }
+ else // EP Bulk OUT.
+ {
+ u32 endpoint_type = usbd_otg->regs->endptctrl[actual_ep] & ~USB2D_ENDPTCTRL_RX_EP_TYPE_MASK;
+ if (actual_ep)
+ {
+ endpoint_type |= usbd_otg->gadget ? USB2D_ENDPTCTRL_RX_EP_TYPE_INTR : USB2D_ENDPTCTRL_RX_EP_TYPE_BULK;
+ }
+ else
+ endpoint_type |= USB2D_ENDPTCTRL_RX_EP_TYPE_CTRL;
+
+ usbd_otg->regs->endptctrl[actual_ep] = endpoint_type;
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_RX_EP_STALL;
+
+ if (actual_ep == USB_HW_EP1)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_RX_EP_RESET;
+
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_RX_EP_ENABLE;
+ }
+}
+
+static int _usbd_initialize_ep0()
+{
+ memset((void *)usbdaemon->qhs, 0, sizeof(dQH_t) * 4); // Clear all used EP queue heads.
+ memset((void *)usbdaemon->dtds, 0, sizeof(dTD_t) * 4); // Clear all used EP0 token heads.
+
+ usbd_otg->regs->asynclistaddr = (u32)usbdaemon->qhs;
+
+ _usbd_initialize_ep_ctrl(USB_EP_CTRL_OUT);
+ _usbd_initialize_ep_ctrl(USB_EP_CTRL_IN);
+
+ // Disable Auto Low Power.
+ usbd_otg->regs->hostpc1_devlc &= ~USB2D_HOSTPC1_DEVLC_ASUS;
+
+ // Initiate an attach event.
+ usbd_otg->regs->usbcmd |= USB2D_USBCMD_RUN;
+
+ u32 retries = 100000; // 200ms timeout.
+ while (!(usbd_otg->regs->usbcmd & USB2D_USBCMD_RUN))
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_TIMEOUT;
+ usleep(1);
+ }
+
+ return USB_RES_OK;
+}
+
+// static void _disable_usb_wdt4()
+// {
+// if (TIMER_WDT4_STATUS & 1)// active
+// {
+// TIMER_TMR0_TMR_PTV &= 0x7FFFFFFF; // Disable timer
+// TIMER_WDT4_UNLOCK_PATTERN = 0xC45A; // Alow writes to disable counter bit.
+// TIMER_WDT4_COMMAND |= 2; // Disable counter
+// TIMER_TMR0_TMR_PCR |= 0x40000000;// INTR_CLR
+// }
+// }
+
+int usbd_flush_endpoint(u32 endpoint)
+{
+
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+ u32 reg_mask = endpoint;
+
+ // Flash all endpoints or 1.
+ if (endpoint != USB_EP_ALL)
+ {
+ if (direction == USB_DIR_IN)
+ reg_mask = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ reg_mask = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+ }
+ usbd_otg->regs->endptflush = reg_mask;
+
+ u32 retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptflush & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_TIMEOUT;
+ usleep(1);
+ }
+
+ // Wait for the endpoint to finish all transactions (buffer not ready).
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptstatus & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_TIMEOUT;
+ usleep(1);
+ }
+
+ // Wait for the endpoint to clear the primed status.
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptprime & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return USB_ERROR_TIMEOUT;
+ usleep(1);
+ }
+
+ return USB_RES_OK;
+}
+
+void usbd_end(bool reset_ep, bool only_controller)
+{
+ if (reset_ep)
+ {
+ usbd_flush_endpoint(USB_EP_ALL);
+ _usbd_stall_reset_ep1(USB_DIR_OUT, USB_EP_CFG_RESET); // EP1 Bulk OUT.
+ _usbd_stall_reset_ep1(USB_DIR_IN, USB_EP_CFG_RESET); // EP1 Bulk IN.
+
+ usbd_otg->config_num = 0;
+ usbd_otg->interface_num = 0;
+ usbd_otg->configuration_set = false;
+ usbd_otg->max_lun_set = false;
+ }
+
+ // Stop device controller.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_RUN;
+
+ // Enable PHY auto low power suspend.
+ usbd_otg->regs->hostpc1_devlc |= USB2D_HOSTPC1_DEVLC_ASUS;
+
+ if (!only_controller)
+ _usb_device_power_down();
+}
+
+static void _usbd_mark_ep_complete(u32 endpoint)
+{
+ u32 complete_bit;
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+
+ usbd_flush_endpoint(endpoint);
+ memset((void *)&usbdaemon->dtds[endpoint * 4], 0, sizeof(dTD_t) * 4);
+ memset((void *)&usbdaemon->qhs[endpoint], 0, sizeof(dQH_t));
+ usbdaemon->ep_configured[endpoint] = 0;
+ usbdaemon->ep_bytes_requested[endpoint] = 0;
+
+ if (direction == USB_DIR_IN)
+ complete_bit = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ complete_bit = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ usbd_otg->regs->endptcomplete |= complete_bit;
+}
+
+static usb_ep_status_t _usbd_get_ep_status(usb_ep_t endpoint)
+{
+ bool status;
+ u32 reg_val;
+ u32 reg_mask;
+ u32 actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+
+ if (direction == USB_DIR_IN)
+ reg_mask = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ reg_mask = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ if (actual_ep == USB_HW_EP1)
+ reg_val = usbd_otg->regs->endptctrl[1];
+ else
+ reg_val = usbd_otg->regs->endptctrl[0];
+
+ // Check stalled status.
+ if (direction == USB_DIR_IN)
+ status = reg_val & USB2D_ENDPTCTRL_TX_EP_STALL;
+ else
+ status = reg_val & USB2D_ENDPTCTRL_RX_EP_STALL;
+
+ if (status)
+ return USB_EP_STATUS_STALLED;
+
+ // Check enabled status.
+ if (direction == USB_DIR_IN)
+ status = reg_val & USB2D_ENDPTCTRL_TX_EP_ENABLE;
+ else
+ status = reg_val & USB2D_ENDPTCTRL_RX_EP_ENABLE;
+
+ if (!status)
+ return USB_EP_STATUS_DISABLED;
+
+ // CHeck qHD error status.
+ u32 token_error_mask = USB_QHD_TOKEN_HALTED | USB_QHD_TOKEN_BUFFER_ERROR | USB_QHD_TOKEN_XFER_ERROR;
+ if (usbdaemon->qhs[endpoint].token & token_error_mask)
+ return USB_EP_STATUS_ERROR;
+
+ // Check if endpoint has a request or a ready buffer.
+ if ((usbd_otg->regs->endptprime & reg_mask) || (usbd_otg->regs->endptstatus & reg_mask))
+ return USB_EP_STATUS_ACTIVE; // RX/TX active.
+
+ // Return idle or not configured status.
+ if (!usbdaemon->ep_configured[endpoint])
+ return USB_EP_STATUS_NO_CONFIG;
+
+ return USB_EP_STATUS_IDLE;
+}
+
+static int _usbd_ep_operation(usb_ep_t endpoint, u8 *buf, u32 len, u32 sync_timeout)
+{
+ if (!buf)
+ len = 0;
+
+ u32 prime_bit;
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+ u32 length_left = len;
+ u32 dtd_ep_idx = endpoint * 4;
+
+ _usbd_mark_ep_complete(endpoint);
+
+ if (endpoint == USB_EP_CTRL_OUT)
+ usbdaemon->qhs[endpoint].ep_capabilities = USB_QHD_EP_CAP_IOS_ENABLE;
+
+ u32 max_packet_len = _usbd_get_max_pkt_length(endpoint) & USB_QHD_EP_CAP_MAX_PKT_LEN_MASK;
+ usbdaemon->qhs[endpoint].ep_capabilities |= (max_packet_len << 16) | USB_QHD_EP_CAP_ZERO_LEN_TERM_DIS;
+ usbdaemon->qhs[endpoint].next_dTD_ptr = 0; // Clear terminate bit.
+ //usbdaemon->qhs[endpoint].ep_capabilities |= USB_QHD_TOKEN_IRQ_ON_COMPLETE;
+
+ usbdaemon->ep_configured[endpoint] = 1;
+ usbdaemon->ep_bytes_requested[endpoint] = len;
+
+ // Configure dTD.
+ u32 dtd_idx = 0;
+ do
+ {
+ if (dtd_idx)
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx - 1].next_dTD = (u32)&usbdaemon->dtds[dtd_ep_idx + dtd_idx];
+
+ u32 dtd_size = MIN(length_left, USB_TD_BUFFER_MAX_SIZE); // 16KB max per dTD.
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].info = (dtd_size << 16) | USB_QHD_TOKEN_ACTIVE;
+ // usbdaemon->dtds[dtd_ep_idx + dtd_idx].info |= USB_QHD_TOKEN_IRQ_ON_COMPLETE;
+
+ // Set buffers addresses to all page pointers.
+ u32 dt_buffer_offset = dtd_idx * USB_TD_BUFFER_MAX_SIZE;
+ for (u32 i = 0; i < 4; i++)
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].pages[i] =
+ (u32)&buf[dt_buffer_offset + (USB_TD_BUFFER_PAGE_SIZE * i)];
+
+ //usbdaemon->dtds[dtd_ep_idx + dtd_idx].pages[5] =
+ // (u32)&buf[dt_buffer_offset + (USB_TD_BUFFER_PAGE_SIZE * 4)]; // Last buffer. Unused.
+
+ length_left -= dtd_size;
+ if (length_left)
+ dtd_idx++;
+ }
+ while (length_left);
+
+ // Last dTD, terminate it.
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].next_dTD = 1;
+
+ // Set first dTD address to queue head next dTD.
+ usbdaemon->qhs[endpoint].next_dTD_ptr |= (u32)&usbdaemon->dtds[dtd_ep_idx] & 0xFFFFFFE0;
+
+ // Flush AHB prefetcher.
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) &= ~MEM_PREFETCH_ENABLE;
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) |= MEM_PREFETCH_ENABLE;
+
+ if (direction == USB_DIR_IN)
+ {
+ prime_bit = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+ }
+ else
+ prime_bit = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ // Prime endpoint.
+ usbd_otg->regs->endptprime |= prime_bit; // USB2_CONTROLLER_USB2D_ENDPTPRIME.
+
+ int res = USB_RES_OK;
+ usb_ep_status_t ep_status;
+ if (sync_timeout)
+ {
+ ep_status = _usbd_get_ep_status(endpoint);
+ if (ep_status == USB_EP_STATUS_ACTIVE)
+ {
+ u32 retries = sync_timeout;
+ while (retries)
+ {
+ ep_status = _usbd_get_ep_status(endpoint);
+ if (ep_status != USB_EP_STATUS_ACTIVE)
+ {
+ if (ep_status == USB_EP_STATUS_DISABLED)
+ res = USB2_ERROR_XFER_EP_DISABLED;
+ goto out;
+ }
+ retries--;
+ usleep(1);
+ }
+ res = USB_ERROR_TIMEOUT;
+ }
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ res = USB2_ERROR_XFER_EP_DISABLED;
+out:
+ if (res)
+ _usbd_mark_ep_complete(endpoint);
+ else if (_usbd_get_ep_status(endpoint) != USB_EP_STATUS_IDLE)
+ res = USB_ERROR_XFER_ERROR;
+
+ if (direction == USB_DIR_OUT)
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+ }
+
+ return res;
+}
+
+static int _usbd_ep_ack(usb_ep_t ep)
+{
+ return _usbd_ep_operation(ep, NULL, 0, USB_XFER_SYNCED_ENUM);
+}
+
+static void _usbd_set_ep0_stall()
+{
+ // EP Control endpoints must be always stalled together.
+ usbd_otg->regs->endptctrl[0] =
+ USB2D_ENDPTCTRL_TX_EP_ENABLE | USB2D_ENDPTCTRL_TX_EP_STALL |
+ USB2D_ENDPTCTRL_RX_EP_ENABLE | USB2D_ENDPTCTRL_RX_EP_STALL;
+}
+
+int usbd_set_ep_stall(u32 endpoint, int ep_stall)
+{
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_dir_t direction = endpoint & 1;
+
+ if (ep_stall)
+ {
+ if (direction == USB_DIR_IN)
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_TX_EP_STALL; // Stall EP Bulk IN.
+ else
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_RX_EP_STALL; // Stall EP Bulk OUT.
+ }
+ else
+ {
+ if (direction == USB_DIR_IN)
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_TX_EP_STALL; // Clear stall EP Bulk IN.
+ else
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_RX_EP_STALL; // Clear stall EP Bulk OUT.
+ }
+
+ return USB_RES_OK;
+}
+
+static void _usbd_handle_get_class_request(bool *transmit_data, u8 *descriptor, int *size, bool *ep_stall)
+{
+ u8 _bRequest = usbd_otg->control_setup.bRequest;
+ u16 _wIndex = usbd_otg->control_setup.wIndex;
+ u16 _wValue = usbd_otg->control_setup.wValue;
+ u16 _wLength = usbd_otg->control_setup.wLength;
+
+ bool valid_interface = _wIndex == usbd_otg->interface_num;
+ bool valid_len = (_bRequest == USB_REQUEST_BULK_GET_MAX_LUN) ? 1 : 0;
+
+ if (!valid_interface || _wValue != 0 || _wLength != valid_len)
+ {
+ *ep_stall = true;
+ return;
+ }
+
+ switch (_bRequest)
+ {
+ case USB_REQUEST_BULK_RESET:
+ _usbd_ep_ack(USB_EP_CTRL_IN);
+ usbd_otg->bulk_reset_req = true;
+ break; // DELAYED_STATUS;
+ case USB_REQUEST_BULK_GET_MAX_LUN:
+ *transmit_data = true;
+ *size = 1;
+ descriptor[0] = usbd_otg->max_lun; // Set 0 LUN for 1 drive supported.
+ usbd_otg->max_lun_set = true;
+ break;
+ default:
+ *ep_stall = true;
+ break;
+ }
+}
+
+static void _usbd_handle_get_descriptor(bool *transmit_data, void **descriptor, int *size, bool *ep_stall)
+{
+ u8 descriptor_type = usbd_otg->control_setup.wValue >> 8;
+ u8 descriptor_subtype = usbd_otg->control_setup.wValue & 0xFF;
+
+ switch (descriptor_type)
+ {
+ case USB_DESCRIPTOR_DEVICE:
+ {
+/*
+ u32 soc_rev = APB_MISC(APB_MISC_GP_HIDREV);
+ usb_device_descriptor.idProduct = (soc_rev >> 8) & 0xFF; // chip_id.
+ usb_device_descriptor.idProduct |= ((soc_rev << 4) | (FUSE(FUSE_SKU_INFO) & 0xF)) << 8; // HIDFAM.
+ usb_device_descriptor.bcdDevice = (soc_rev >> 16) & 0xF; // MINORREV.
+ usb_device_descriptor.bcdDevice |= ((soc_rev >> 4) & 0xF) << 8; // MAJORREV.
+*/
+ *descriptor = usbd_otg->desc->dev;
+ *size = usbd_otg->desc->dev->bLength;
+ *transmit_data = true;
+ return;
+ }
+ case USB_DESCRIPTOR_CONFIGURATION:
+ if (usbd_otg->gadget == USB_GADGET_UMS)
+ {
+ if (usbd_otg->port_speed == USB_HIGH_SPEED) // High speed. 512 bytes.
+ {
+ usbd_otg->desc->cfg->endpoint[0].wMaxPacketSize = 0x200;
+ usbd_otg->desc->cfg->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ else // Full speed. 64 bytes.
+ {
+ usbd_otg->desc->cfg->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_otg->desc->cfg->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ }
+ else
+ {
+ usb_cfg_hid_descr_t *tmp = (usb_cfg_hid_descr_t *)usbd_otg->desc->cfg;
+ if (usbd_otg->port_speed == USB_HIGH_SPEED) // High speed. 512 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x200;
+ tmp->endpoint[1].wMaxPacketSize = 0x200;
+ tmp->endpoint[0].bInterval = usbd_otg->gadget == USB_GADGET_HID_GAMEPAD ? 4 : 3; // 8ms : 4ms.
+ tmp->endpoint[1].bInterval = usbd_otg->gadget == USB_GADGET_HID_GAMEPAD ? 4 : 3; // 8ms : 4ms.
+ }
+ else // Full speed. 64 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x40;
+ tmp->endpoint[1].wMaxPacketSize = 0x40;
+ tmp->endpoint[0].bInterval = usbd_otg->gadget == USB_GADGET_HID_GAMEPAD ? 8 : 4; // 8ms : 4ms.
+ tmp->endpoint[1].bInterval = usbd_otg->gadget == USB_GADGET_HID_GAMEPAD ? 8 : 4; // 8ms : 4ms.
+ }
+ }
+ *descriptor = usbd_otg->desc->cfg;
+ *size = usbd_otg->desc->cfg->config.wTotalLength;
+ *transmit_data = true;
+ return;
+ case USB_DESCRIPTOR_STRING:
+ switch (descriptor_subtype)
+ {
+ case 1:
+ *descriptor = usbd_otg->desc->vendor;
+ *size = usbd_otg->desc->vendor[0];
+ break;
+ case 2:
+ *descriptor = usbd_otg->desc->product;
+ *size = usbd_otg->desc->product[0];
+ break;
+ case 3:
+ *descriptor = usbd_otg->desc->serial;
+ *size = usbd_otg->desc->serial[0];
+ break;
+ case 0xEE:
+ *descriptor = usbd_otg->desc->ms_os;
+ *size = usbd_otg->desc->ms_os->bLength;
+ break;
+ default:
+ *descriptor = usbd_otg->desc->lang_id;
+ *size = 4;
+ break;
+ }
+ *transmit_data = true;
+ return;
+ case USB_DESCRIPTOR_DEVICE_QUALIFIER:
+ if (!usbd_otg->desc->dev_qual)
+ goto exit;
+ usbd_otg->desc->dev_qual->bNumOtherConfigs = 1;
+ *descriptor = usbd_otg->desc->dev_qual;
+ *size = usbd_otg->desc->dev_qual->bLength;
+ *transmit_data = true;
+ return;
+ case USB_DESCRIPTOR_OTHER_SPEED_CONFIGURATION:
+ if (!usbd_otg->desc->cfg_other)
+ goto exit;
+ if (usbd_otg->port_speed == USB_HIGH_SPEED)
+ {
+ usbd_otg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_otg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ else
+ {
+ usbd_otg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x200;
+ usbd_otg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ if ((usbd_otg->charger_detect & 1) && (usbd_otg->charger_detect & 2))
+ usbd_otg->desc->cfg_other->config.bMaxPower = 500 / 2;
+ *descriptor = usbd_otg->desc->cfg_other;
+ *size = usbd_otg->desc->cfg_other->config.wTotalLength;
+ *transmit_data = true;
+ return;
+ case USB_DESCRIPTOR_DEVICE_BINARY_OBJECT:
+ *descriptor = usbd_otg->desc->dev_bot;
+ *size = usbd_otg->desc->dev_bot->wTotalLength;
+ *transmit_data = true;
+ return;
+ default:
+ *transmit_data = false;
+ *ep_stall = true;
+ return;
+ }
+exit:
+ *transmit_data = false;
+ *ep_stall = true;
+ return;
+}
+
+static int _usbd_handle_set_request(bool *ep_stall)
+{
+ int res = USB_RES_OK;
+ u8 bRequest = usbd_otg->control_setup.bRequest;
+ if (bRequest == USB_REQUEST_SET_ADDRESS)
+ {
+ res = _usbd_ep_ack(USB_EP_CTRL_IN);
+
+ // Set USB address for device mode.
+ if (!res)
+ usbd_otg->regs->periodiclistbase = (usbd_otg->regs->periodiclistbase & 0x1FFFFFF) | ((usbd_otg->control_setup.wValue & 0xFF) << 25);
+ }
+ else if (bRequest == USB_REQUEST_SET_CONFIGURATION)
+ {
+ res = _usbd_ep_ack(USB_EP_CTRL_IN);
+ if (!res)
+ {
+ usbd_otg->config_num = usbd_otg->control_setup.wValue;
+ _usbd_initialize_ep_ctrl(USB_EP_BULK_OUT);
+ _usbd_initialize_ep_ctrl(USB_EP_BULK_IN);
+ usbd_otg->configuration_set = true;
+ }
+ }
+ else
+ *ep_stall = true;
+
+ return res;
+}
+
+static int _usbd_handle_ep0_control_transfer()
+{
+ int res = USB_RES_OK;
+ bool ep_stall = false;
+ bool transmit_data = false;
+
+ u8 *descriptor = (u8 *)USB_DESCRIPTOR_ADDR;
+ int size = 0;
+
+ u8 _bmRequestType = usbd_otg->control_setup.bmRequestType;
+ u8 _bRequest = usbd_otg->control_setup.bRequest;
+ u16 _wValue = usbd_otg->control_setup.wValue;
+ u16 _wIndex = usbd_otg->control_setup.wIndex;
+ u16 _wLength = usbd_otg->control_setup.wLength;
+
+ //gfx_printf("%02X %02X %04X %04X %04X\n", _bmRequestType, _bRequest, _wValue, _wIndex, _wLength);
+
+ switch (_bmRequestType)
+ {
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE):
+ res = _usbd_handle_set_request(&ep_stall);
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE):
+ res = _usbd_ep_ack(USB_EP_CTRL_IN);
+ if (!res)
+ usbd_otg->interface_num = _wValue;
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT):
+ switch (_bRequest)
+ {
+ case USB_REQUEST_CLEAR_FEATURE:
+ case USB_REQUEST_SET_FEATURE:
+ if ((_wValue & 0xFF) == USB_FEATURE_ENDPOINT_HALT)
+ {
+ int direction;
+ switch (_wIndex) // endpoint
+ {
+ case USB_EP_ADDR_CTRL_OUT:
+ direction = 2;
+ break;
+ case USB_EP_ADDR_CTRL_IN:
+ direction = 3;
+ break;
+ case USB_EP_ADDR_BULK_OUT:
+ direction = 0;
+ break;
+ case USB_EP_ADDR_BULK_IN:
+ direction = 1;
+ break;
+ default:
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ goto out;
+ }
+
+ if (_bRequest == USB_REQUEST_CLEAR_FEATURE)
+ _usbd_stall_reset_ep1(direction, USB_EP_CFG_RESET);
+ else
+ _usbd_stall_reset_ep1(direction, USB_EP_CFG_STALL);
+
+ res = _usbd_ep_ack(USB_EP_CTRL_IN);
+ }
+ else
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+
+ break;
+ default:
+ ep_stall = true;
+ break;
+ }
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE):
+ memset(descriptor, 0, _wLength);
+ _usbd_handle_get_class_request(&transmit_data, descriptor, &size, &ep_stall);
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE):
+ switch (_bRequest)
+ {
+ case USB_REQUEST_GET_STATUS:
+ descriptor[0] = USB_STATUS_DEV_SELF_POWERED;
+ descriptor[1] = 0; // No support for remove wake up.
+ transmit_data = true;
+ size = 2;
+ break;
+ case USB_REQUEST_GET_DESCRIPTOR:
+ _usbd_handle_get_descriptor(&transmit_data, (void **)&descriptor, &size, &ep_stall);
+ break;
+ case USB_REQUEST_GET_CONFIGURATION:
+ descriptor = (u8 *)&usbd_otg->config_num;
+ size = _wLength;
+ transmit_data = true;
+ break;
+ default:
+ ep_stall = true;
+ break;
+ }
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE):
+ if (_bRequest == USB_REQUEST_GET_INTERFACE)
+ {
+ memset(descriptor, 0, _wLength);
+ descriptor[0] = usbd_otg->interface_num;
+ size = _wLength;
+ }
+ else if (_bRequest == USB_REQUEST_GET_STATUS)
+ {
+ memset(descriptor, 0, _wLength);
+ size = _wLength;
+ }
+ else if (_bRequest == USB_REQUEST_GET_DESCRIPTOR && (_wValue >> 8) == USB_DESCRIPTOR_HID_REPORT && usbd_otg->gadget > USB_GADGET_UMS)
+ {
+ if (usbd_otg->gadget == USB_GADGET_HID_GAMEPAD)
+ {
+ descriptor = (u8 *)&hid_report_descriptor_jc;
+ size = hid_report_descriptor_jc_size;
+ }
+ else // USB_GADGET_HID_TOUCHPAD
+ {
+ descriptor = (u8 *)&hid_report_descriptor_touch;
+ size = hid_report_descriptor_touch_size;
+ }
+
+ usbd_otg->hid_report_sent = true;
+ }
+ else
+ {
+ ep_stall = true;
+ break;
+ }
+
+ if (_wLength < size)
+ size = _wLength;
+ transmit_data = true;
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT):
+ if (_bRequest == USB_REQUEST_GET_STATUS)
+ {
+ int ep_req;
+ switch (_wIndex)
+ {
+ case USB_EP_ADDR_CTRL_OUT:
+ ep_req = USB_EP_CTRL_OUT;
+ break;
+ case USB_EP_ADDR_BULK_OUT:
+ ep_req = USB_EP_BULK_OUT;
+ break;
+ case USB_EP_ADDR_CTRL_IN:
+ ep_req = USB_EP_CTRL_IN;
+ break;
+ case USB_EP_ADDR_BULK_IN:
+ ep_req = USB_EP_BULK_IN;
+ break;
+ default:
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ goto out;
+ }
+
+ size = _wLength;
+ memset(descriptor, 0, size);
+
+ if (_usbd_get_ep_status(ep_req) == USB_EP_STATUS_STALLED)
+ descriptor[0] = USB_STATUS_EP_HALTED;
+ else
+ descriptor[0] = USB_STATUS_EP_OK;
+
+ transmit_data = true;
+ }
+ else
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE):
+ memset(descriptor, 0, _wLength);
+ _usbd_handle_get_class_request(&transmit_data, descriptor, &size, &ep_stall);
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_INTERFACE):
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE):
+ if (_bRequest == USB_REQUEST_GET_MS_DESCRIPTOR)
+ {
+ switch (_wIndex)
+ {
+ case USB_DESCRIPTOR_MS_COMPAT_ID:
+ descriptor = (u8 *)usbd_otg->desc->ms_cid;
+ size = usbd_otg->desc->ms_cid->dLength;
+ transmit_data = true;
+ break;
+ case USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES:
+ descriptor = (u8 *)usbd_otg->desc->mx_ext;
+ size = usbd_otg->desc->mx_ext->dLength;
+ transmit_data = true;
+ break;
+ default:
+ ep_stall = true;
+ break;
+ }
+ }
+ else
+ ep_stall = true;
+ break;
+
+ default:
+ ep_stall = true;
+ break;
+ }
+
+ // Transmit data to HOST if any.
+ if (transmit_data)
+ {
+ memcpy(usb_ep0_ctrl_buf, descriptor, size);
+
+ if (_wLength < size)
+ size = _wLength;
+ res = _usbd_ep_operation(USB_EP_CTRL_IN, usb_ep0_ctrl_buf, size, USB_XFER_SYNCED_ENUM);
+ if (!res)
+ res = _usbd_ep_ack(USB_EP_CTRL_OUT);
+ }
+
+out:
+ if (ep_stall)
+ _usbd_set_ep0_stall();
+
+ return res;
+}
+
+static int _usbd_ep0_initialize()
+{
+ bool enter = false;
+ if (usbd_otg->configuration_set)
+ enter = true;
+ else
+ {
+ usbdaemon->qhs = (volatile dQH_t *)USB2_QH_USB2D_QH_EP_BASE;
+
+ if (!_usbd_initialize_ep0())
+ enter = true;
+ }
+
+ if (enter)
+ {
+ usbd_otg->configuration_set = false;
+ usbd_otg->max_lun_set = false;
+
+ // Timeout if cable or communication isn't started in 1.5 minutes.
+ u32 timer = get_tmr_ms() + 90000;
+ while (true)
+ {
+ u32 usb_status_irqs = usbd_otg->regs->usbsts;
+
+ // Clear all interrupt statuses.
+ usbd_otg->regs->usbsts = usb_status_irqs;
+
+ // Check if a reset was received.
+ if (usb_status_irqs & USB2D_USBSTS_URI)
+ {
+ //_disable_usb_wdt4();
+
+ // Clear all device addresses, enabled setup requests, transmit events and flush all endpoints.
+ usbd_otg->regs->periodiclistbase = 0;
+ usbd_otg->regs->endptsetupstat = usbd_otg->regs->endptsetupstat;
+ usbd_otg->regs->endptcomplete = usbd_otg->regs->endptcomplete;
+ usbd_flush_endpoint(USB_EP_ALL);
+ }
+
+ // Check if port change happened.
+ if (usb_status_irqs & USB2D_USBSTS_PCI)
+ usbd_otg->port_speed = (usbd_otg->regs->hostpc1_devlc & USB2D_HOSTPC1_DEVLC_PSPD_MASK) >> 25;
+
+ // Acknowledge setup request for EP0 and copy its configuration.
+ u32 ep0_setup_req = usbd_otg->regs->endptsetupstat;
+ if (ep0_setup_req & 1)
+ {
+ usbd_otg->regs->endptsetupstat = ep0_setup_req;
+ memcpy(&usbd_otg->control_setup, (void *)usbdaemon->qhs->setup, 8);
+ if (_usbd_handle_ep0_control_transfer())
+ break;
+ }
+ if (usbd_otg->configuration_set)
+ return USB_RES_OK;
+
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return USB_ERROR_USER_ABORT;
+ }
+ }
+
+ return USB_ERROR_TIMEOUT;
+}
+
+int usb_device_enumerate(usb_gadget_type gadget)
+{
+ switch (gadget)
+ {
+ case USB_GADGET_UMS:
+ usbd_otg->desc = &usb_gadget_ums_descriptors;
+ break;
+ case USB_GADGET_HID_GAMEPAD:
+ usbd_otg->desc = &usb_gadget_hid_jc_descriptors;
+ break;
+ case USB_GADGET_HID_TOUCHPAD:
+ usbd_otg->desc = &usb_gadget_hid_touch_descriptors;
+ break;
+ }
+
+ usbd_otg->gadget = gadget;
+
+ return _usbd_ep0_initialize();
+}
+
+int usbd_handle_ep0_ctrl_setup()
+{
+ // Acknowledge setup request for EP0 and copy its configuration.
+ u32 ep0_setup_req = usbd_otg->regs->endptsetupstat;
+ if (ep0_setup_req & 1)
+ {
+ usbd_otg->regs->endptsetupstat = ep0_setup_req;
+ memcpy(&usbd_otg->control_setup, (void *)usbdaemon->qhs->setup, 8);
+ _usbd_handle_ep0_control_transfer();
+ memset(usb_ep0_ctrl_buf, 0, USB_TD_BUFFER_PAGE_SIZE);
+ }
+
+ // Only return error if bulk reset was requested.
+ if (usbd_otg->bulk_reset_req)
+ {
+ usbd_otg->bulk_reset_req = false;
+ return USB_RES_BULK_RESET;
+ }
+
+ return USB_RES_OK;
+}
+
+static usb_ep_status_t _usbd_get_ep1_status(usb_dir_t dir)
+{
+ usb_ep_t ep;
+ if (dir == USB_DIR_OUT)
+ ep = USB_EP_BULK_OUT;
+ else
+ ep = USB_EP_BULK_IN;
+ return _usbd_get_ep_status(ep);
+}
+
+int usb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, u32 sync_timeout)
+{
+ if ((u32)buf % USB_EP_BUFFER_ALIGN)
+ return USB2_ERROR_XFER_NOT_ALIGNED;
+
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ int res = _usbd_ep_operation(USB_EP_BULK_OUT, buf, len, sync_timeout);
+
+ if (sync_timeout && bytes_read)
+ *bytes_read = res ? 0 : len;
+
+ return res;
+}
+
+int usb_device_ep1_out_read_big(u8 *buf, u32 len, u32 *bytes_read)
+{
+ if ((u32)buf % USB_EP_BUFFER_ALIGN)
+ return USB2_ERROR_XFER_NOT_ALIGNED;
+
+ if (len > USB_EP_BULK_OUT_MAX_XFER)
+ len = USB_EP_BULK_OUT_MAX_XFER;
+
+ int res;
+ u32 bytes = 0;
+ *bytes_read = 0;
+ u8 *buf_curr = buf;
+
+ while (len)
+ {
+ u32 len_ep = MIN(len, USB_EP_BUFFER_MAX_SIZE);
+
+ res = usb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED_DATA);
+ if (res)
+ return res;
+
+ len -= len_ep;
+ buf_curr += len_ep;
+ *bytes_read = *bytes_read + bytes;
+ }
+
+ return USB_RES_OK;
+}
+
+static int _usbd_get_ep1_out_bytes_read()
+{
+ if (_usbd_get_ep_status(USB_EP_BULK_OUT) != USB_EP_STATUS_IDLE)
+ return 0;
+ else
+ return (usbdaemon->ep_bytes_requested[USB_EP_BULK_OUT] - (usbdaemon->qhs[USB_EP_BULK_OUT].token >> 16));
+}
+
+int usb_device_ep1_out_reading_finish(u32 *pending_bytes)
+{
+ usb_ep_status_t ep_status;
+ do
+ {
+ ep_status = _usbd_get_ep1_status(USB_DIR_OUT);
+ if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED))
+ break;
+
+ usbd_handle_ep0_ctrl_setup();
+ }
+ while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED));
+
+ *pending_bytes = _usbd_get_ep1_out_bytes_read();
+
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+
+ if (ep_status == USB_EP_STATUS_IDLE)
+ return USB_RES_OK;
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ return USB2_ERROR_XFER_EP_DISABLED;
+ else
+ return USB_ERROR_XFER_ERROR;
+}
+
+int usb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, u32 sync_timeout)
+{
+ if ((u32)buf % USB_EP_BUFFER_ALIGN)
+ return USB2_ERROR_XFER_NOT_ALIGNED;
+
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ int res = _usbd_ep_operation(USB_EP_BULK_IN, buf, len, sync_timeout);
+
+ if (sync_timeout && bytes_written)
+ *bytes_written = res ? 0 : len;
+
+ return res;
+}
+
+static int _usbd_get_ep1_in_bytes_written()
+{
+ if (_usbd_get_ep_status(USB_EP_BULK_IN) != USB_EP_STATUS_IDLE)
+ return 0;
+ else
+ return (usbdaemon->ep_bytes_requested[USB_EP_BULK_IN] - (usbdaemon->qhs[USB_EP_BULK_IN].token >> 16));
+}
+
+int usb_device_ep1_in_writing_finish(u32 *pending_bytes)
+{
+ usb_ep_status_t ep_status;
+ do
+ {
+ ep_status = _usbd_get_ep1_status(USB_DIR_IN);
+ if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED))
+ break;
+
+ usbd_handle_ep0_ctrl_setup();
+ }
+ while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED));
+
+ *pending_bytes = _usbd_get_ep1_in_bytes_written();
+
+ if (ep_status == USB_EP_STATUS_IDLE)
+ return USB_RES_OK;
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ return USB2_ERROR_XFER_EP_DISABLED;
+
+ usb_device_stall_ep1_bulk_out();
+ return USB_ERROR_XFER_ERROR;
+}
+
+bool usb_device_get_suspended()
+{
+ bool suspended = (usbd_otg->regs->portsc1 & USB2D_PORTSC1_SUSP) == USB2D_PORTSC1_SUSP;
+ return suspended;
+}
+
+bool usb_device_get_port_in_sleep()
+{
+ // Windows heuristic: Forces port into suspend, sleep and J-State.
+ return (usbd_otg->regs->portsc1) == 0x885;
+}
+
+int usb_device_class_send_max_lun(u8 max_lun)
+{
+ // Timeout if get MAX_LUN request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ usbd_otg->max_lun = max_lun;
+
+ while (!usbd_otg->max_lun_set)
+ {
+ usbd_handle_ep0_ctrl_setup();
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return USB_ERROR_USER_ABORT;
+ }
+
+ return USB_RES_OK;
+}
+
+int usb_device_class_send_hid_report()
+{
+ // Timeout if get GET_HID_REPORT request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ // Wait for request and transfer start.
+ while (!usbd_otg->hid_report_sent)
+ {
+ usbd_handle_ep0_ctrl_setup();
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return USB_ERROR_USER_ABORT;
+ }
+
+ return USB_RES_OK;
+}
+
+void usb_device_get_ops(usb_ops_t *ops)
+{
+ ops->usbd_flush_endpoint = usbd_flush_endpoint;
+ ops->usbd_set_ep_stall = usbd_set_ep_stall;
+ ops->usbd_handle_ep0_ctrl_setup = usbd_handle_ep0_ctrl_setup;
+ ops->usbd_end = usbd_end;
+ ops->usb_device_init = usb_device_init;
+ ops->usb_device_enumerate = usb_device_enumerate;
+ ops->usb_device_class_send_max_lun = usb_device_class_send_max_lun;
+ ops->usb_device_class_send_hid_report = usb_device_class_send_hid_report;
+ ops->usb_device_get_suspended = usb_device_get_suspended;
+ ops->usb_device_get_port_in_sleep = usb_device_get_port_in_sleep;
+
+ ops->usb_device_ep1_out_read = usb_device_ep1_out_read;
+ ops->usb_device_ep1_out_read_big = usb_device_ep1_out_read_big;
+ ops->usb_device_ep1_out_reading_finish = usb_device_ep1_out_reading_finish;
+ ops->usb_device_ep1_in_write = usb_device_ep1_in_write;
+ ops->usb_device_ep1_in_writing_finish = usb_device_ep1_in_writing_finish;
+}
+
diff --git a/bdk/usb/usbd.h b/bdk/usb/usbd.h
new file mode 100644
index 00000000..a0e4a63a
--- /dev/null
+++ b/bdk/usb/usbd.h
@@ -0,0 +1,203 @@
+/*
+ * Enhanced & eXtensible USB Device (EDCI & XDCI) driver for Tegra X1
+ *
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include
+
+#define USB_TD_BUFFER_PAGE_SIZE 0x1000
+#define USB_TD_BUFFER_MAX_SIZE (USB_TD_BUFFER_PAGE_SIZE * 4)
+//#define USB_HW_BUFFER_5_PAGES 0x5000
+#define USB_EP_BUFFER_1_TD (USB_TD_BUFFER_MAX_SIZE)
+#define USB_EP_BUFFER_2_TD (USB_TD_BUFFER_MAX_SIZE * 2)
+#define USB_EP_BUFFER_4_TD (USB_TD_BUFFER_MAX_SIZE * 4)
+#define USB_EP_BUFFER_MAX_SIZE (USB_EP_BUFFER_4_TD)
+#define USB_EP_BUFFER_ALIGN (USB_TD_BUFFER_PAGE_SIZE)
+
+#define USB_XFER_START 0
+#define USB_XFER_SYNCED_ENUM 1000000
+#define USB_XFER_SYNCED_CMD 1000000
+#define USB_XFER_SYNCED_DATA 2000000
+#define USB_XFER_SYNCED_CLASS 5000000
+#define USB_XFER_SYNCED -1
+
+typedef enum _usb_hid_type
+{
+ USB_HID_GAMEPAD,
+ USB_HID_TOUCHPAD
+} usb_hid_type;
+
+typedef enum _usb_gadget_type
+{
+ USB_GADGET_UMS = 0,
+ USB_GADGET_HID_GAMEPAD = 1,
+ USB_GADGET_HID_TOUCHPAD = 2,
+} usb_gadget_type;
+
+typedef enum {
+ USB_DIR_OUT = 0,
+ USB_DIR_IN = 1,
+} usb_dir_t;
+
+typedef enum
+{
+ XUSB_EP_CTRL_IN = 0, // EP0.
+ XUSB_EP_CTRL_OUT = 1, // EP0.
+
+ USB_EP_CTRL_OUT = 0, // EP0.
+ USB_EP_CTRL_IN = 1, // EP0.
+
+ USB_EP_BULK_OUT = 2, // EP1.
+ USB_EP_BULK_IN = 3, // EP1.
+ USB_EP_ALL = 0xFFFFFFFF
+} usb_ep_t;
+
+typedef enum
+{
+ USB_EP_ADDR_CTRL_OUT = 0x00,
+ USB_EP_ADDR_CTRL_IN = 0x80,
+ USB_EP_ADDR_BULK_OUT = 0x01,
+ USB_EP_ADDR_BULK_IN = 0x81,
+} usb_ep_addr_t;
+
+typedef enum
+{
+ USB_EP_CFG_CLEAR = 0,
+ USB_EP_CFG_RESET = 0,
+ USB_EP_CFG_STALL = 1
+} usb_ep_cfg_t;
+
+typedef enum {
+ USB_STATUS_EP_OK = 0,
+ USB_STATUS_EP_HALTED = 1,
+
+ USB_STATUS_DEV_SELF_POWERED = 1,
+ USB_STATUS_DEV_REMOTE_WAKE = 2,
+} usb_set_clear_feature_req_t;
+
+typedef enum {
+ USB_SETUP_RECIPIENT_DEVICE = 0,
+ USB_SETUP_RECIPIENT_INTERFACE = 1,
+ USB_SETUP_RECIPIENT_ENDPOINT = 2,
+ USB_SETUP_RECIPIENT_OTHER = 3,
+
+ USB_SETUP_TYPE_STANDARD = 0x00,
+ USB_SETUP_TYPE_CLASS = 0x20,
+ USB_SETUP_TYPE_VENDOR = 0x40,
+ USB_SETUP_TYPE_RESERVED = 0x60,
+
+ USB_SETUP_HOST_TO_DEVICE = 0x00,
+ USB_SETUP_DEVICE_TO_HOST = 0x80,
+} usb_setup_req_type_t;
+
+typedef enum {
+ USB_REQUEST_GET_STATUS = 0,
+ USB_REQUEST_CLEAR_FEATURE = 1,
+ USB_REQUEST_SET_FEATURE = 3,
+ USB_REQUEST_SET_ADDRESS = 5,
+ USB_REQUEST_GET_DESCRIPTOR = 6,
+ USB_REQUEST_SET_DESCRIPTOR = 7,
+ USB_REQUEST_GET_CONFIGURATION = 8,
+ USB_REQUEST_SET_CONFIGURATION = 9,
+ USB_REQUEST_GET_INTERFACE = 10,
+ USB_REQUEST_SET_INTERFACE = 11,
+ USB_REQUEST_SYNCH_FRAME = 12,
+ USB_REQUEST_SET_SEL = 13,
+
+ USB_REQUEST_GET_MS_DESCRIPTOR = 0x99,
+
+ USB_REQUEST_BULK_GET_MAX_LUN = 0xFE,
+ USB_REQUEST_BULK_RESET = 0xFF
+} usb_standard_req_t;
+
+typedef enum {
+ USB_FEATURE_ENDPOINT_HALT = 0,
+ USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1,
+ USB_FEATURE_TEST_MODE = 2,
+} usb_get_status_req_t;
+
+typedef enum _usb_error_t
+{
+ USB_RES_OK = 0,
+ USB_RES_BULK_RESET = 1,
+
+ USB_ERROR_USER_ABORT = 2,
+ USB_ERROR_TIMEOUT = 3,
+ USB_ERROR_INIT = 4,
+ USB_ERROR_XFER_ERROR = 5,
+
+ USB2_ERROR_XFER_EP_DISABLED = 28,
+ USB2_ERROR_XFER_NOT_ALIGNED = 29,
+
+ XUSB_ERROR_INVALID_EP = USB_ERROR_XFER_ERROR, // From 2.
+ XUSB_ERROR_XFER_BULK_IN_RESIDUE = 7,
+ XUSB_ERROR_INVALID_CYCLE = USB2_ERROR_XFER_EP_DISABLED, // From 8.
+ XUSB_ERROR_SEQ_NUM = 51,
+ XUSB_ERROR_XFER_DIR = 52,
+ XUSB_ERROR_PORT_CFG = 54
+} usb_error_t;
+
+typedef struct _usb_ctrl_setup_t
+{
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+} usb_ctrl_setup_t;
+
+typedef struct _usb_ops_t
+{
+ int (*usbd_flush_endpoint)(u32);
+ int (*usbd_set_ep_stall)(u32, int);
+ int (*usbd_handle_ep0_ctrl_setup)();
+ void (*usbd_end)(bool, bool);
+ int (*usb_device_init)();
+ int (*usb_device_enumerate)(usb_gadget_type gadget);
+ int (*usb_device_class_send_max_lun)(u8);
+ int (*usb_device_class_send_hid_report)();
+
+ int (*usb_device_ep1_out_read)(u8 *, u32, u32 *, u32);
+ int (*usb_device_ep1_out_read_big)(u8 *, u32, u32 *);
+ int (*usb_device_ep1_out_reading_finish)(u32 *);
+ int (*usb_device_ep1_in_write)(u8 *, u32, u32 *, u32);
+ int (*usb_device_ep1_in_writing_finish)(u32 *);
+ bool (*usb_device_get_suspended)();
+ bool (*usb_device_get_port_in_sleep)();
+} usb_ops_t;
+
+typedef struct _usb_ctxt_t
+{
+ u32 type;
+ u32 partition;
+ u32 offset;
+ u32 sectors;
+ u32 ro;
+ void (*system_maintenance)(bool);
+ void *label;
+ void (*set_text)(void *, const char *);
+} usb_ctxt_t;
+
+void usb_device_get_ops(usb_ops_t *ops);
+void xusb_device_get_ops(usb_ops_t *ops);
+
+int usb_device_gadget_ums(usb_ctxt_t *usbs);
+int usb_device_gadget_hid(usb_ctxt_t *usbs);
+
+#endif
\ No newline at end of file
diff --git a/bdk/usb/xusbd.c b/bdk/usb/xusbd.c
new file mode 100644
index 00000000..4beefcdb
--- /dev/null
+++ b/bdk/usb/xusbd.c
@@ -0,0 +1,2026 @@
+/*
+ * eXtensible USB Device driver (XDCI) for Tegra X1
+ *
+ * Copyright (c) 2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define XUSB_TRB_SLOTS 16 //! TODO: Consider upping it.
+#define XUSB_LINK_TRB_IDX (XUSB_TRB_SLOTS - 1)
+#define XUSB_LAST_TRB_IDX (XUSB_TRB_SLOTS - 1)
+
+#define EP_DONT_RING 0
+#define EP_RING_DOORBELL 1
+
+typedef enum {
+ XUSB_FULL_SPEED = 1,
+ XUSB_HIGH_SPEED = 3,
+ XUSB_SUPER_SPEED = 4
+} xusb_speed_t;
+
+typedef enum {
+ EP_DISABLED = 0,
+ EP_RUNNING = 1,
+ EP_HALTED = 2,
+ EP_STOPPED = 3,
+ EP_ERROR = 4
+} xusb_ep_status_t;
+
+typedef enum {
+ EP_TYPE_ISOC_OUT = 1,
+ EP_TYPE_BULK_OUT = 2,
+ EP_TYPE_INTR_OUT = 3,
+ EP_TYPE_CNTRL = 4,
+ EP_TYPE_ISOC_IN = 5,
+ EP_TYPE_BULK_IN = 6,
+ EP_TYPE_INTR_IN = 7
+} xusb_ep_type_t;
+
+typedef enum {
+ XUSB_DEFAULT = 0,
+ XUSB_ADDRESSED_STS_WAIT = 1,
+ XUSB_ADDRESSED = 2,
+ XUSB_CONFIGURED_STS_WAIT = 3,
+ XUSB_CONFIGURED = 4,
+
+ XUSB_LUN_CONFIGURED_STS_WAIT = 5,
+ XUSB_LUN_CONFIGURED = 6,
+ XUSB_HID_CONFIGURED_STS_WAIT = 7,
+ XUSB_HID_CONFIGURED = 8,
+
+ // XUSB_CONNECTED = ,
+ // XUSB_DISCONNECTED = ,
+ // XUSB_RESET = ,
+ // XUSB_SUSPENDED = ,
+} xusb_dev_state_t;
+
+typedef enum {
+ XUSB_TRB_NONE = 0,
+ XUSB_TRB_NORMAL = 1,
+ XUSB_TRB_DATA = 3,
+ XUSB_TRB_STATUS = 4,
+ XUSB_TRB_LINK = 6,
+ XUSB_TRB_TRANSFER = 32,
+ XUSB_TRB_PORT_CHANGE = 34,
+ XUSB_TRB_SETUP = 63,
+} xusb_trb_type_t;
+
+typedef enum {
+ XUSB_COMP_INVALID = 0,
+ XUSB_COMP_SUCCESS = 1,
+ XUSB_COMP_DATA_BUFFER_ERROR = 2,
+ XUSB_COMP_BABBLE_DETECTED_ERROR = 3,
+ XUSB_COMP_USB_TRANSACTION_ERROR = 4,
+ XUSB_COMP_TRB_ERROR = 5,
+ XUSB_COMP_STALL_ERROR = 6,
+ XUSB_COMP_RESOURCE_ERROR = 7,
+ XUSB_COMP_BANDWIDTH_ERROR = 8,
+ XUSB_COMP_NO_SLOTS_AVAILABLE_ERROR = 9,
+ XUSB_COMP_INVALID_STREAM_TYPE_ERROR = 10,
+ XUSB_COMP_SLOT_NOT_ENABLED_ERROR = 11,
+ XUSB_COMP_EP_DISABLED_ERROR = 12,
+ XUSB_COMP_SHORT_PKT = 13,
+ XUSB_COMP_RING_UNDERRUN = 14,
+ XUSB_COMP_RING_OVERRUN = 15,
+ XUSB_COMP_VF_EVENT_RING_FULL_ERROR = 16,
+ XUSB_COMP_PARAMETER_ERROR = 17,
+ XUSB_COMP_BANDWIDTH_OVERRUN_ERROR = 18,
+ XUSB_COMP_CONTEXT_STATE_ERROR = 19,
+ XUSB_COMP_NO_PING_RESPONSE_ERROR = 20,
+ XUSB_COMP_EVENT_RING_FULL_ERROR = 21,
+ XUSB_COMP_INCOMPATIBLE_DEVICE_ERROR = 22,
+ XUSB_COMP_MISSED_SERVICE_ERROR = 23,
+ XUSB_COMP_COMMAND_RING_STOPPED = 24,
+ XUSB_COMP_COMMAND_ABORTED = 25,
+ XUSB_COMP_STOPPED = 26,
+ XUSB_COMP_STOPPED_LENGTH_INVALID = 27,
+ XUSB_COMP_STOPPED_SHORT_PACKET = 28,
+ XUSB_COMP_EXIT_LATENCY_LARGE_ERROR = 29,
+ XUSB_COMP_ISOCH_BUFFER_OVERRUN = 31,
+ XUSB_COMP_EVENT_LOST_ERROR = 32,
+ XUSB_COMP_UNDEFINED_ERROR = 33,
+ XUSB_COMP_INVALID_STREAM_ID_ERROR = 34,
+ XUSB_COMP_SECONDARY_BANDWIDTH_ERROR = 35,
+ XUSB_COMP_SPLIT_TRANSACTION_ERROR = 36,
+
+ XUSB_COMP_CODE_STREAM_NUMP_ERROR = 219,
+ XUSB_COMP_PRIME_PIPE_RECEIVED = 220,
+ XUSB_COMP_HOST_REJECTED = 221,
+ XUSB_COMP_CTRL_DIR_ERROR = 222,
+ XUSB_COMP_CTRL_SEQ_NUM_ERROR = 223
+} xusb_comp_code_t;
+
+typedef struct _event_trb_t
+{
+ u32 rsvd0;
+ u32 rsvd1;
+
+ u32 rsvd2:24;
+ u32 comp_code:8;
+
+ u32 cycle:1;
+ u32 rsvd3:9;
+ u32 trb_type:6;
+ u32 ep_id:5;
+ u32 rsvd4:11;
+} event_trb_t;
+
+typedef struct _transfer_event_trb_t {
+ u32 trb_pointer_lo;
+ u32 trb_pointer_hi;
+
+ u32 trb_tx_len:24;
+ u32 comp_code:8;
+
+ u32 cycle:1;
+ u32 rsvddw3_0:1;
+ u32 event_data:1;
+ u32 rsvddw3_1:7;
+ u32 trb_type:6;
+ u32 ep_id:5;
+ u32 rsvddw3_2:11;
+} transfer_event_trb_t;
+
+typedef struct _setup_event_trb_t
+{
+ usb_ctrl_setup_t ctrl_setup_data;
+
+ u32 ctrl_seq_num:16;
+ u32 rsvddw2_0:8;
+ u32 comp_code:8;
+
+ u32 cycle:1;
+ u32 rsvddw3_0:9;
+ u32 trb_type:6;
+ u32 ep_id:5;
+ u32 rsvddw3_1:11;
+} setup_event_trb_t;
+
+typedef struct _status_trb_t
+{
+ u32 rsvd0;
+ u32 rsvd1;
+
+ u32 rsvd2:22;
+ u32 interrupt_target:10;
+
+ u32 cycle:1;
+ u32 ent:1;
+ u32 rsvd3_0:2;
+ u32 chain:1;
+ u32 ioc:1;
+ u32 rsvd3_1:4;
+ u32 trb_type:6;
+ u32 dir:1;
+ u32 rsvd3_2:15;
+} status_trb_t;
+
+typedef struct _normal_trb_t
+{
+ u32 databufptr_lo;
+ u32 databufptr_hi;
+
+ u32 trb_tx_len:17;
+ u32 td_size:5;
+ u32 interrupt_target:10;
+
+ u32 cycle:1;
+ u32 ent:1;
+ u32 isp:1;
+ u32 no_snoop:1;
+ u32 chain:1;
+ u32 ioc:1;
+ u32 idt:1;
+ u32 rsvd0_0:2;
+ u32 bei:1;
+ u32 trb_type:6;
+ u32 rsvd0_1:16;
+} normal_trb_t;
+
+typedef struct _data_trb_t
+{
+ u32 databufptr_lo;
+ u32 databufptr_hi;
+
+ u32 trb_tx_len:17;
+ u32 td_size:5;
+ u32 interrupt_target:10;
+
+ u32 cycle:1;
+ u32 ent:1;
+ u32 isp:1;
+ u32 no_snoop:1;
+ u32 chain:1;
+ u32 ioc:1;
+ u32 rsvd0_0:4;
+ u32 trb_type:6;
+ u32 dir:1;
+ u32 rsvd0_1:15;
+} data_trb_t;
+
+typedef struct _link_trb_t
+{
+ u32 rsvd0_0:4;
+ u32 ring_seg_ptrlo:28;
+
+ u32 ring_seg_ptrhi;
+
+ u32 rsvd1_0:22;
+ u32 interrupt_target:10;
+
+ u32 cycle:1;
+ u32 toggle_cycle:1;
+ u32 rsvd3_0:2;
+ u32 chain:1;
+ u32 ioc:1;
+ u32 rsvd3_1:4;
+ u32 trb_type:6;
+ u32 rsvd3_2:16;
+} link_trb_t;
+
+typedef struct _xusb_ep_ctx_t
+{
+ // Common context.
+ u32 ep_state:3;
+ u32 rsvddW0_0:5;
+ u32 mult:2;
+ u32 max_pstreams:5;
+ u32 lsa:1;
+ u32 interval:8;
+ u32 rsvddW0_1:8;
+
+ u32 rsvddw1_0:1;
+ u32 cerr:2;
+ u32 ep_type:3;
+ u32 rsvddw1_1:1;
+ u32 hid:1;
+ u32 max_burst_size:8;
+ u32 max_packet_size:16;
+
+ u32 dcs:1;
+ u32 rsvddw2_0:3;
+ u32 trd_dequeueptr_lo:28;
+
+ u32 trd_dequeueptr_hi;
+
+ u32 avg_trb_len:16;
+ u32 max_esit_payload:16;
+
+ // Nvidia context.
+ u32 event_data_txlen_acc;
+
+ u32 cprog:8;
+ u32 sbyte:7;
+ u32 tp:2;
+ u32 rec:1;
+ u32 cec:2;
+ u32 ced:1;
+ u32 hsp1:1;
+ u32 rty1:1;
+ u32 std:1;
+ u32 status:8;
+
+ u32 data_offset;
+
+ u32 scratch_pad0;
+
+ u32 scratch_pad1;
+
+ u32 cping:8;
+ u32 sping:8;
+ u32 toggle_cycle:2;
+ u32 no_snoop:1;
+ u32 ro:1;
+ u32 tlm:1;
+ u32 dlm:1;
+ u32 hsp2:1;
+ u32 rty2:1;
+ u32 stop_rec_req:8;
+
+ u32 device_addr:8;
+ u32 hub_addr:8;
+ u32 root_port_num:8;
+ u32 slot_id:8;
+
+ u32 routing_string:20;
+ u32 speed:4;
+ u32 lpu:1;
+ u32 mtt:1;
+ u32 hub:1;
+ u32 dci:5;
+
+ u32 tthub_slot_id:8;
+ u32 ttport_num:8;
+ u32 ssf:4;
+ u32 sps:2;
+ u32 interrupt_target:10;
+
+ u32 frz:1;
+ u32 end:1;
+ u32 elm:1;
+ u32 mrx:1;
+ u32 ep_linklo:28;
+
+ u32 ep_linkhi;
+} xusb_ep_ctx_t;
+
+typedef struct _xusbd_controller_t
+{
+ data_trb_t *cntrl_epenqueue_ptr;
+ data_trb_t *cntrl_epdequeue_ptr;
+ u32 cntrl_producer_cycle;
+ data_trb_t *bulkout_epenqueue_ptr;
+ data_trb_t *bulkout_epdequeue_ptr;
+ u32 bulkout_producer_cycle;
+ data_trb_t *bulkin_epenqueue_ptr;
+ data_trb_t *bulkin_epdequeue_ptr;
+ u32 bulkin_producer_cycle;
+ event_trb_t *event_enqueue_ptr;
+ event_trb_t *event_dequeue_ptr;
+ u32 event_ccs;
+ u32 device_state;
+ u32 bytes_remaining[2];
+ u32 tx_count[2];
+ u32 ctrl_seq_num;
+ u32 config_num;
+ u32 interface_num;
+ u32 wait_for_event_trb;
+ u32 port_speed;
+
+ usb_desc_t *desc;
+ usb_gadget_type gadget;
+
+ u8 max_lun;
+ bool max_lun_set;
+ bool bulk_reset_req;
+} xusbd_controller_t;
+
+extern u32 hid_report_descriptor_jc_size;
+extern u32 hid_report_descriptor_touch_size;
+extern u8 hid_report_descriptor_jc[];
+extern u8 hid_report_descriptor_touch[];
+extern usb_desc_t usb_gadget_hid_jc_descriptors;
+extern usb_desc_t usb_gadget_hid_touch_descriptors;
+extern usb_desc_t usb_gadget_ums_descriptors;
+
+// All rings and EP context must be aligned to 0x10.
+typedef struct _xusbd_event_queues_t
+{
+ event_trb_t xusb_event_ring_seg0[XUSB_TRB_SLOTS];
+ event_trb_t xusb_event_ring_seg1[XUSB_TRB_SLOTS];
+ data_trb_t xusb_cntrl_event_queue[XUSB_TRB_SLOTS];
+ data_trb_t xusb_bulkin_event_queue[XUSB_TRB_SLOTS];
+ data_trb_t xusb_bulkout_event_queue[XUSB_TRB_SLOTS];
+ volatile xusb_ep_ctx_t xusb_ep_ctxt[4];
+} xusbd_event_queues_t;
+
+// Set event queues context to a 0x10 aligned address.
+xusbd_event_queues_t *xusb_evtq = (xusbd_event_queues_t *)XUSB_RING_ADDR;
+
+xusbd_controller_t *usbd_xotg;
+xusbd_controller_t usbd_xotg_controller_ctxt;
+
+static int _xusb_xhci_mask_wait(u32 reg, u32 mask, u32 val, u32 retries)
+{
+ do
+ {
+ if ((XUSB_DEV_XHCI(reg) & mask) == val)
+ return USB_RES_OK;
+ usleep(1);
+ --retries;
+ }
+ while (retries);
+
+ return USB_ERROR_TIMEOUT;
+}
+
+// Event rings aligned to 0x10
+static void _xusbd_ep_init_event_ring()
+{
+ memset(xusb_evtq->xusb_event_ring_seg0, 0, sizeof(xusb_evtq->xusb_event_ring_seg0));
+ memset(xusb_evtq->xusb_event_ring_seg1, 0, sizeof(xusb_evtq->xusb_event_ring_seg1));
+
+ //! TODO USB3: enable pcie regulators.
+
+ // Set Event Ring Segment 0 Base Address.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERST0BALO) = (u32)xusb_evtq->xusb_event_ring_seg0;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERST0BAHI) = 0;
+
+ // Set Event Ring Segment 1 Base Address.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERST1BALO) = (u32)xusb_evtq->xusb_event_ring_seg1;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERST1BAHI) = 0;
+
+ // Set Event Ring Segment sizes.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERSTSZ) = (XUSB_TRB_SLOTS << 16) | XUSB_TRB_SLOTS;
+
+ // Set Enqueue and Dequeue pointers.
+ usbd_xotg->event_enqueue_ptr = xusb_evtq->xusb_event_ring_seg0;
+ usbd_xotg->event_dequeue_ptr = xusb_evtq->xusb_event_ring_seg0;
+ usbd_xotg->event_ccs = 1;
+
+ // Event Ring Enqueue Pointer.
+ u32 evt_ring_addr = (u32)xusb_evtq->xusb_event_ring_seg0 & 0xFFFFFFF0;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EREPLO) = (XUSB_DEV_XHCI(XUSB_DEV_XHCI_EREPLO) & 0xE) | evt_ring_addr | XCHI_ECS;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EREPHI) = 0;
+
+ // Set Event Ring Dequeue Pointer.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERDPLO) = (XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERDPLO) & 0xF) | evt_ring_addr;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERDPHI) = 0;
+}
+
+static void _xusb_ep_set_type_and_metrics(u32 ep_idx, volatile xusb_ep_ctx_t *ep_ctxt)
+{
+ usb_ep_descr_t *ep_desc = NULL;
+ usb_ep_descr_t *endpoints = usbd_xotg->desc->cfg->endpoint;
+
+ switch (ep_idx)
+ {
+ case XUSB_EP_CTRL_IN:
+ // Set EP type.
+ ep_ctxt->ep_type = EP_TYPE_CNTRL;
+
+ // Set max packet size based on port speed.
+ ep_ctxt->avg_trb_len = 8;
+ ep_ctxt->max_packet_size = 64; //! TODO USB3: max_packet_size = 512.
+ break;
+
+ case USB_EP_BULK_OUT:
+ // Set default EP type.
+ ep_ctxt->ep_type = EP_TYPE_BULK_OUT;
+
+ // Check configuration descriptor.
+ if (usbd_xotg->desc->cfg->interface.bInterfaceClass == 0x3) // HID Class.
+ endpoints = (usb_ep_descr_t *)((void *)endpoints + sizeof(usb_hid_descr_t));
+
+ for (u32 i = 0; i < usbd_xotg->desc->cfg->interface.bNumEndpoints; i++)
+ if (endpoints[i].bEndpointAddress == USB_EP_ADDR_BULK_OUT)
+ {
+ ep_desc = &endpoints[i];
+ break;
+ }
+
+ // Set actual EP type.
+ if (ep_desc)
+ {
+ switch (ep_desc->bmAttributes)
+ {
+ case USB_EP_TYPE_ISO:
+ ep_ctxt->ep_type = EP_TYPE_ISOC_OUT;
+ break;
+ case USB_EP_TYPE_BULK:
+ ep_ctxt->ep_type = EP_TYPE_BULK_OUT;
+ break;
+ case USB_EP_TYPE_INTR:
+ ep_ctxt->ep_type = EP_TYPE_INTR_OUT;
+ break;
+ }
+ }
+
+ // Set average TRB length.
+ //TODO: Use ep type instead (we don't expect to calculate avg per gadget)?
+ switch (usbd_xotg->gadget)
+ {
+ case USB_GADGET_UMS:
+ ep_ctxt->avg_trb_len = 3072;
+ break;
+ case USB_GADGET_HID_GAMEPAD:
+ case USB_GADGET_HID_TOUCHPAD:
+ ep_ctxt->avg_trb_len = 1024;
+ break;
+ default:
+ switch (usbd_xotg->port_speed)
+ {
+ case XUSB_SUPER_SPEED:
+ ep_ctxt->avg_trb_len = 1024;
+ break;
+ case XUSB_HIGH_SPEED:
+ case XUSB_FULL_SPEED:
+ ep_ctxt->avg_trb_len = 512;
+ break;
+ }
+ break;
+ }
+
+ // Set max burst rate.
+ ep_ctxt->max_burst_size = (ep_desc->wMaxPacketSize >> 11) & 3;
+
+ // Set max packet size based on port speed.
+ if (usbd_xotg->port_speed == XUSB_SUPER_SPEED)
+ {
+ ep_ctxt->max_packet_size = 1024;
+
+ //! TODO USB3:
+ // If ISO or INTR EP, set Max Esit Payload size.
+ // ep_ctxt->max_burst_size = bMaxBurst;
+ //if (ep_ctxt->ep_type == EP_TYPE_INTR_OUT || ep_ctxt->ep_type == EP_TYPE_ISOC_OUT)
+ // ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size * (ep_ctxt->max_burst_size + 1);
+ }
+ else if (usbd_xotg->port_speed == XUSB_HIGH_SPEED)
+ {
+ ep_ctxt->max_packet_size = 512;
+
+ // If ISO or INTR EP, set Max Esit Payload size.
+ if (ep_ctxt->ep_type == EP_TYPE_INTR_OUT || ep_ctxt->ep_type == EP_TYPE_ISOC_OUT)
+ ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size * (ep_ctxt->max_burst_size + 1);
+ }
+ else
+ {
+ ep_ctxt->max_packet_size = 64;
+
+ // If ISO or INTR EP, set Max Esit Payload size.
+ if (ep_ctxt->ep_type == EP_TYPE_INTR_OUT || ep_ctxt->ep_type == EP_TYPE_ISOC_OUT)
+ ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size;
+ }
+ break;
+
+ case USB_EP_BULK_IN:
+ // Set default EP type.
+ ep_ctxt->ep_type = EP_TYPE_BULK_IN;
+
+ // Check configuration descriptor.
+ if (usbd_xotg->desc->cfg->interface.bInterfaceClass == 0x3) // HID Class.
+ endpoints = (usb_ep_descr_t *)((void *)endpoints + sizeof(usb_hid_descr_t));
+
+ for (u32 i = 0; i < usbd_xotg->desc->cfg->interface.bNumEndpoints; i++)
+ if (endpoints[i].bEndpointAddress == USB_EP_ADDR_BULK_IN)
+ {
+ ep_desc = &endpoints[i];
+ break;
+ }
+
+ // Set actual EP type.
+ if (ep_desc)
+ {
+ switch (ep_desc->bmAttributes)
+ {
+ case USB_EP_TYPE_ISO:
+ ep_ctxt->ep_type = EP_TYPE_ISOC_IN;
+ break;
+ case USB_EP_TYPE_BULK:
+ ep_ctxt->ep_type = EP_TYPE_BULK_IN;
+ break;
+ case USB_EP_TYPE_INTR:
+ ep_ctxt->ep_type = EP_TYPE_INTR_IN;
+ break;
+ }
+ }
+
+ // Set average TRB length.
+ //TODO: Use ep type instead (we don't expect to calculate avg per gadget)?
+ switch (usbd_xotg->gadget)
+ {
+ case USB_GADGET_UMS:
+ ep_ctxt->avg_trb_len = 3072;
+ break;
+ case USB_GADGET_HID_GAMEPAD:
+ case USB_GADGET_HID_TOUCHPAD:
+ ep_ctxt->avg_trb_len = 16; // Normal interrupt avg is 1024KB.
+ break;
+ default:
+ switch (usbd_xotg->port_speed)
+ {
+ case XUSB_SUPER_SPEED:
+ ep_ctxt->avg_trb_len = 1024;
+ break;
+ case XUSB_HIGH_SPEED:
+ case XUSB_FULL_SPEED:
+ ep_ctxt->avg_trb_len = 512;
+ break;
+ }
+ break;
+ }
+
+ // Set max burst rate.
+ ep_ctxt->max_burst_size = (ep_desc->wMaxPacketSize >> 11) & 3;
+
+ // Set max packet size based on port speed.
+ if (usbd_xotg->port_speed == XUSB_SUPER_SPEED)
+ {
+ ep_ctxt->max_packet_size = 1024;
+
+ //! TODO USB3:
+ // If ISO or INTR EP, set Max Esit Payload size.
+ // ep_ctxt->max_burst_size = bMaxBurst;
+ //if (ep_ctxt->ep_type == EP_TYPE_INTR_IN || ep_ctxt->ep_type == EP_TYPE_ISOC_IN)
+ // ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size * (ep_ctxt->max_burst_size + 1);
+ }
+ else if (usbd_xotg->port_speed == XUSB_HIGH_SPEED)
+ {
+ ep_ctxt->max_packet_size = 512;
+
+ // If ISO or INTR EP, set Max Esit Payload size.
+ if (ep_ctxt->ep_type == EP_TYPE_INTR_IN || ep_ctxt->ep_type == EP_TYPE_ISOC_IN)
+ ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size * (ep_ctxt->max_burst_size + 1);
+ }
+ else
+ {
+ ep_ctxt->max_packet_size = 64;
+
+ // If ISO or INTR EP, set Max Esit Payload size.
+ if (ep_ctxt->ep_type == EP_TYPE_INTR_IN || ep_ctxt->ep_type == EP_TYPE_ISOC_IN)
+ ep_ctxt->max_esit_payload = ep_ctxt->max_packet_size;
+ }
+ break;
+ }
+}
+
+static int _xusb_ep_init_context(u32 ep_idx)
+{
+ link_trb_t *link_trb;
+
+ if (ep_idx > USB_EP_BULK_IN)
+ return USB_ERROR_INIT;
+
+ if (ep_idx == XUSB_EP_CTRL_OUT)
+ ep_idx = XUSB_EP_CTRL_IN;
+
+ volatile xusb_ep_ctx_t *ep_ctxt = &xusb_evtq->xusb_ep_ctxt[ep_idx];
+ memset((void *)ep_ctxt, 0, sizeof(xusb_ep_ctx_t));
+
+ ep_ctxt->ep_state = EP_RUNNING;
+ ep_ctxt->dcs = 1;
+ ep_ctxt->cec = 3;
+ ep_ctxt->cerr = 3;
+ ep_ctxt->max_burst_size = 0;
+
+ switch (ep_idx)
+ {
+ case XUSB_EP_CTRL_IN:
+ usbd_xotg->cntrl_producer_cycle = 1;
+ usbd_xotg->cntrl_epenqueue_ptr = xusb_evtq->xusb_cntrl_event_queue;
+ usbd_xotg->cntrl_epdequeue_ptr = xusb_evtq->xusb_cntrl_event_queue;
+
+ _xusb_ep_set_type_and_metrics(ep_idx, ep_ctxt);
+
+ ep_ctxt->trd_dequeueptr_lo = (u32)xusb_evtq->xusb_cntrl_event_queue >> 4;
+ ep_ctxt->trd_dequeueptr_hi = 0;
+
+ link_trb = (link_trb_t *)&xusb_evtq->xusb_cntrl_event_queue[XUSB_LINK_TRB_IDX];
+ link_trb->toggle_cycle = 1;
+ link_trb->ring_seg_ptrlo = (u32)xusb_evtq->xusb_cntrl_event_queue >> 4;
+ link_trb->ring_seg_ptrhi = 0;
+ link_trb->trb_type = XUSB_TRB_LINK;
+ break;
+
+ case USB_EP_BULK_OUT:
+ usbd_xotg->bulkout_producer_cycle = 1;
+ usbd_xotg->bulkout_epenqueue_ptr = xusb_evtq->xusb_bulkout_event_queue;
+ usbd_xotg->bulkout_epdequeue_ptr = xusb_evtq->xusb_bulkout_event_queue;
+
+ _xusb_ep_set_type_and_metrics(ep_idx, ep_ctxt);
+
+ ep_ctxt->trd_dequeueptr_lo = (u32)xusb_evtq->xusb_bulkout_event_queue >> 4;
+ ep_ctxt->trd_dequeueptr_hi = 0;
+
+ link_trb = (link_trb_t *)&xusb_evtq->xusb_bulkout_event_queue[XUSB_LINK_TRB_IDX];
+ link_trb->toggle_cycle = 1;
+ link_trb->ring_seg_ptrlo = (u32)xusb_evtq->xusb_bulkout_event_queue >> 4;
+ link_trb->ring_seg_ptrhi = 0;
+ link_trb->trb_type = XUSB_TRB_LINK;
+ break;
+
+ case USB_EP_BULK_IN:
+ usbd_xotg->bulkin_producer_cycle = 1;
+ usbd_xotg->bulkin_epenqueue_ptr = xusb_evtq->xusb_bulkin_event_queue;
+ usbd_xotg->bulkin_epdequeue_ptr = xusb_evtq->xusb_bulkin_event_queue;
+
+ _xusb_ep_set_type_and_metrics(ep_idx, ep_ctxt);
+
+ ep_ctxt->trd_dequeueptr_lo = (u32)xusb_evtq->xusb_bulkin_event_queue >> 4;
+ ep_ctxt->trd_dequeueptr_hi = 0;
+
+ link_trb = (link_trb_t *)&xusb_evtq->xusb_bulkin_event_queue[XUSB_LINK_TRB_IDX];
+ link_trb->toggle_cycle = 1;
+ link_trb->ring_seg_ptrlo = (u32)xusb_evtq->xusb_bulkin_event_queue >> 4;
+ link_trb->ring_seg_ptrhi = 0;
+ link_trb->trb_type = XUSB_TRB_LINK;
+ break;
+ }
+
+ return USB_RES_OK;
+}
+
+static int _xusbd_ep_initialize(u32 ep_idx)
+{
+ switch (ep_idx)
+ {
+ case XUSB_EP_CTRL_IN:
+ case XUSB_EP_CTRL_OUT:
+ return _xusb_ep_init_context(XUSB_EP_CTRL_IN);
+ case USB_EP_BULK_OUT:
+ case USB_EP_BULK_IN:
+ _xusb_ep_init_context(ep_idx);
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_RELOAD) = BIT(ep_idx);
+ int res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_EP_RELOAD, BIT(ep_idx), 0, 1000);
+ if (!res)
+ {
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_PAUSE) &= ~BIT(ep_idx);
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_HALT) &= ~BIT(ep_idx);
+ }
+ return res;
+ default:
+ return USB_ERROR_INIT;
+ }
+}
+
+static void _xusb_init_phy()
+{
+ // Configure and enable PLLU.
+ clock_enable_pllu();
+
+ // Enable IDDQ control by software and disable UTMIPLL IDDQ.
+ CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) = (CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) & 0xFFFFFFFC) | 1;
+
+ // Set UTMIPLL dividers and config based on OSC and enable it to 960 MHz.
+ clock_enable_utmipll();
+
+ // Set UTMIP misc config.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) & 0xFEFFFFE8) | 0x2000008 | 0x20 | 2;
+ usleep(2);
+
+ // Set OTG PAD0 calibration.
+ u32 fuse_usb_calib = FUSE(FUSE_USB_CALIB);
+ // Set HS_CURR_LEVEL.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_0) = (XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_0) & 0xFFFFFFC0) | (fuse_usb_calib & 0x3F);
+ // Set TERM_RANGE_ADJ and RPD_CTRL.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_1) = (XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_1) & 0x83FFFF87) | ((fuse_usb_calib & 0x780) >> 4) | ((u32)(FUSE(FUSE_USB_CALIB_EXT) << 27) >> 1);
+
+ // Set VREG_LEV to 1.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1) = (XUSB_PADCTL(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL1) & 0xFFFFFE3F) | 0x80;
+
+ // Disable power down on usb2 ports pads.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_0) &= 0xDBFFFFFF; // Clear pad power down.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_1) &= 0xFFFFFFFB; // Clear pad dr power down.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD0_CTL0) &= 0xFFFFFFFE; // Clear charging power down.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_0) &= 0xFFFFF7FF; // Clear bias power down.
+ (void)XUSB_PADCTL(XUSB_PADCTL_USB2_OTG_PAD0_CTL_1); // Commit write.
+
+ // Enable USB2 tracking clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_Y_SET) = BIT(CLK_Y_USB2_TRK);
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) & 0xFFFFFF00) | 6; // Set trank divisor to 4.
+
+ // Set tracking parameters and trigger it.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_1) = 0x451E000;
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_1) = 0x51E000;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_1) = 0x451E000;
+ usleep(3);
+
+ // Re-trigger it.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_1) = 0x51E000;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_BIAS_PAD_CTL_1) |= 0x4000000;
+
+ // Disable USB2 tracking clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_Y_CLR) = BIT(CLK_Y_USB2_TRK);
+
+ // Wait for XUSB PHY to stabilize.
+ usleep(30);
+}
+
+static void _xusbd_init_device_clocks()
+{
+ // Disable reset to PLLU_OUT1
+ CLOCK(CLK_RST_CONTROLLER_PLLU_OUTA) |= 1;
+ usleep(2);
+
+ // Enable XUSB device clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_SET) = BIT(CLK_U_XUSB_DEV);
+
+ // Set XUSB device core clock source to PLLP for a 102MHz result.
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_CORE_DEV) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_CORE_DEV) & 0x1FFFFF00) | (1 << 29) | 6;
+ usleep(2);
+
+ // Set XUSB Full-Speed logic clock source to FO 48MHz.
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_FS) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_FS) & 0x1FFFFFFF) | (2 << 29);
+
+ // Enable XUSB Super-Speed logic clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_SET) = BIT(CLK_W_XUSB_SS);
+
+ // Set XUSB Super-Speed logic clock source to HSIC 480MHz for 120MHz result and source FS logic clock from Super-Speed.
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_SS) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_XUSB_SS) & 0x1FFFFF00) | (3 << 29) | 6;
+
+ // Clear reset to XUSB device and Super-Speed logic.
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_CLR) = BIT(CLK_W_XUSB_SS);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_CLR) = BIT(CLK_U_XUSB_DEV);
+ usleep(2);
+}
+
+int xusb_device_init()
+{
+ /////////////////////////////////////////////////
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_USBD);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_USBD);
+ /////////////////////////////////////////////////
+
+
+ // Enable XUSB clock and clear Reset to XUSB Pad Control.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_SET) = BIT(CLK_W_XUSB);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB);
+ usleep(2);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_CLR) = BIT(CLK_W_XUSB);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_CLR) = BIT(CLK_W_XUSB_PADCTL);
+ usleep(2);
+
+ // USB2 Pads to XUSB.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_PAD_MUX) =
+ (XUSB_PADCTL(XUSB_PADCTL_USB2_PAD_MUX) & ~(PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK | PADCTL_USB2_PAD_MUX_USB2_OTG_PAD_PORT0_MASK)) |
+ PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB | PADCTL_USB2_PAD_MUX_USB2_OTG_PAD_PORT0_XUSB;
+
+ // Initialize XUSB controller PHY.
+ _xusb_init_phy();
+
+ // Set USB2.0 Port 0 to device mode.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_PORT_CAP) = (XUSB_PADCTL(XUSB_PADCTL_USB2_PORT_CAP) & ~PADCTL_USB2_PORT_CAP_PORT_0_CAP_MASK) | PADCTL_USB2_PORT_CAP_PORT_0_CAP_DEV;
+
+ //! TODO USB3
+ // // Set USB3.0 Port 0 cap to device.
+ // XUSB_PADCTL(XUSB_PADCTL_SS_PORT_CAP) = (XUSB_PADCTL(XUSB_PADCTL_SS_PORT_CAP) & ~PADCTL_SS_PORT_CAP_0_PORT1_CAP_MASK) | PADCTL_SS_PORT_CAP_0_PORT1_CAP_DEVICE_ONLY;
+
+ // Set Super Speed Port 0 to USB2 Port 0.
+ XUSB_PADCTL(XUSB_PADCTL_SS_PORT_MAP) &= ~PADCTL_SS_PORT_MAP_PORT0_MASK; // 0: USB2_PORT0
+
+ // Power Up ID Wake up and Vbus Wake Up for UTMIP
+ PMC(APBDEV_PMC_USB_AO) &= 0xFFFFFFF3;
+ usleep(1);
+
+ // Initialize device clocks.
+ _xusbd_init_device_clocks();
+
+ // Enable AHB redirect for access to IRAM for Event/EP ring buffers.
+ mc_enable_ahb_redirect(); // Can be skipped if IRAM is not used.
+
+ // Enable XUSB device IPFS.
+ XUSB_DEV_DEV(XUSB_DEV_CONFIGURATION) |= DEV_CONFIGURATION_EN_FPCI;
+
+ // Configure PCI and BAR0 address space.
+ XUSB_DEV_PCI(XUSB_CFG_1) |= CFG_1_BUS_MASTER | CFG_1_MEMORY_SPACE | CFG_1_IO_SPACE;
+ usleep(1);
+ XUSB_DEV_PCI(XUSB_CFG_4) = XUSB_DEV_BASE | CFG_4_ADDRESS_TYPE_32_BIT;
+
+ // Mask SATA interrupt to MCORE.
+ XUSB_DEV_DEV(XUSB_DEV_INTR_MASK) |= DEV_INTR_MASK_IP_INT_MASK;
+
+ // AHB USB performance cfg.
+ AHB_GIZMO(AHB_GIZMO_AHB_MEM) |= AHB_MEM_DONT_SPLIT_AHB_WR | AHB_MEM_ENB_FAST_REARBITRATE;
+ AHB_GIZMO(AHB_GIZMO_USB3) |= AHB_GIZMO_IMMEDIATE;
+ AHB_GIZMO(AHB_ARBITRATION_PRIORITY_CTRL) = PRIORITY_CTRL_WEIGHT(7) | PRIORITY_SELECT_USB3;
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) =
+ MEM_PREFETCH_ENABLE | MEM_PREFETCH_USB3_MST_ID | MEM_PREFETCH_ADDR_BNDRY(12) | 0x1000; // Addr boundary 64KB, Inactivity 4096 cycles.
+
+ // Initialize context.
+ usbd_xotg = &usbd_xotg_controller_ctxt;
+ memset(usbd_xotg, 0, sizeof(xusbd_controller_t));
+
+ // Initialize event and EP rings.
+ _xusbd_ep_init_event_ring();
+ memset(xusb_evtq->xusb_cntrl_event_queue, 0, sizeof(xusb_evtq->xusb_cntrl_event_queue));
+ memset(xusb_evtq->xusb_bulkin_event_queue, 0, sizeof(xusb_evtq->xusb_bulkin_event_queue));
+ memset(xusb_evtq->xusb_bulkout_event_queue, 0, sizeof(xusb_evtq->xusb_bulkout_event_queue));
+
+ // Initialize Control EP.
+ int res = _xusbd_ep_initialize(XUSB_EP_CTRL_IN);
+ if (res)
+ return USB_ERROR_INIT;
+
+ // Enable events and interrupts.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CTRL) |= XHCI_CTRL_IE | XHCI_CTRL_LSE;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ECPLO) = (u32)xusb_evtq->xusb_ep_ctxt & 0xFFFFFFF0;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ECPHI) = 0;
+
+ //! TODO USB3:
+ // XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT) |= DEV_XHCI_PORTHALT_STCHG_INTR_EN;
+
+ return USB_RES_OK;
+}
+
+static int _xusb_queue_trb(int ep_idx, void *trb, bool ring_doorbell)
+{
+ int res = USB_RES_OK;
+ data_trb_t *next_trb;
+ link_trb_t *link_trb;
+
+ // Copy TRB and advance Enqueue list.
+ switch (ep_idx)
+ {
+ case XUSB_EP_CTRL_IN:
+ memcpy(usbd_xotg->cntrl_epenqueue_ptr, trb, sizeof(data_trb_t));
+
+ // Advance queue and if Link TRB set index to 0 and toggle cycle bit.
+ next_trb = &usbd_xotg->cntrl_epenqueue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ {
+ link_trb = (link_trb_t *)next_trb;
+ link_trb->cycle = usbd_xotg->cntrl_producer_cycle & 1;
+ link_trb->toggle_cycle = 1;
+ next_trb = (data_trb_t *)(link_trb->ring_seg_ptrlo << 4);
+ usbd_xotg->cntrl_producer_cycle ^= 1;
+ }
+ usbd_xotg->cntrl_epenqueue_ptr = next_trb;
+ break;
+
+ case USB_EP_BULK_OUT:
+ memcpy(usbd_xotg->bulkout_epenqueue_ptr, trb, sizeof(data_trb_t));
+
+ // Advance queue and if Link TRB set index to 0 and toggle cycle bit.
+ next_trb = &usbd_xotg->bulkout_epenqueue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ {
+ link_trb = (link_trb_t *)next_trb;
+ link_trb->cycle = usbd_xotg->bulkout_producer_cycle & 1;
+ link_trb->toggle_cycle = 1;
+ next_trb = (data_trb_t *)(link_trb->ring_seg_ptrlo << 4);
+ usbd_xotg->bulkout_producer_cycle ^= 1;
+ }
+ usbd_xotg->bulkout_epenqueue_ptr = next_trb;
+ break;
+
+ case USB_EP_BULK_IN:
+ memcpy(usbd_xotg->bulkin_epenqueue_ptr, trb, sizeof(data_trb_t));
+
+ // Advance queue and if Link TRB set index to 0 and toggle cycle bit.
+ next_trb = &usbd_xotg->bulkin_epenqueue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ {
+ link_trb = (link_trb_t *)next_trb;
+ link_trb->cycle = usbd_xotg->bulkin_producer_cycle & 1;
+ link_trb->toggle_cycle = 1;
+ next_trb = (data_trb_t *)(link_trb->ring_seg_ptrlo << 4);
+ usbd_xotg->bulkin_producer_cycle ^= 1;
+ }
+ usbd_xotg->bulkin_epenqueue_ptr = next_trb;
+ break;
+
+ case XUSB_EP_CTRL_OUT:
+ default:
+ res = XUSB_ERROR_INVALID_EP;
+ break;
+ }
+
+ // Ring doorbell.
+ if (ring_doorbell)
+ {
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+ u32 target_id = (ep_idx << 8) & 0xFFFF;
+ if (ep_idx == XUSB_EP_CTRL_IN)
+ target_id |= usbd_xotg->ctrl_seq_num << 16;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_DB) = target_id;
+ }
+
+ return res;
+}
+
+static void _xusb_create_status_trb(status_trb_t *trb, usb_dir_t direction)
+{
+ trb->cycle = usbd_xotg->cntrl_producer_cycle & 1;
+ trb->ioc = 1; // Enable interrupt on completion.
+ trb->trb_type = XUSB_TRB_STATUS;
+ trb->dir = direction;
+}
+
+static void _xusb_create_normal_trb(normal_trb_t *trb, u8 *buf, u32 len, usb_dir_t direction)
+{
+ u8 producer_cycle;
+
+ trb->databufptr_lo = (u32)buf;
+ trb->databufptr_hi = 0;
+
+ trb->trb_tx_len = len;
+
+ // Single TRB transfer.
+ trb->td_size = 0;
+ trb->chain = 0;
+
+ if (direction == USB_DIR_IN)
+ producer_cycle = usbd_xotg->bulkin_producer_cycle & 1;
+ else
+ producer_cycle = usbd_xotg->bulkout_producer_cycle & 1;
+
+ trb->cycle = producer_cycle;
+ trb->isp = 1; // Enable interrupt on short packet.
+ trb->ioc = 1; // Enable interrupt on completion.
+ trb->trb_type = XUSB_TRB_NORMAL;
+}
+
+static void _xusb_create_data_trb(data_trb_t *trb, u8 *buf, u32 len, usb_dir_t direction)
+{
+ trb->databufptr_lo = (u32)buf;
+ trb->databufptr_hi = 0;
+
+ trb->trb_tx_len = len;
+
+ // Single TRB transfer.
+ trb->td_size = 0;
+ trb->chain = 0;
+
+ trb->cycle = usbd_xotg->cntrl_producer_cycle & 1;
+ trb->isp = 1; // Enable interrupt on short packet.
+ trb->ioc = 1; // Enable interrupt on completion.
+ trb->trb_type = XUSB_TRB_DATA;
+ trb->dir = direction;
+}
+
+static int _xusb_issue_status_trb(usb_dir_t direction)
+{
+ int res = USB_RES_OK;
+ status_trb_t trb = {0};
+
+ if (usbd_xotg->cntrl_epenqueue_ptr == usbd_xotg->cntrl_epdequeue_ptr || direction == USB_DIR_OUT)
+ {
+ _xusb_create_status_trb(&trb, direction);
+ res = _xusb_queue_trb(XUSB_EP_CTRL_IN, &trb, EP_RING_DOORBELL);
+ usbd_xotg->wait_for_event_trb = XUSB_TRB_STATUS;
+ }
+
+ return res;
+}
+
+static int _xusb_issue_normal_trb(u8 *buf, u32 len, usb_dir_t direction)
+{
+ normal_trb_t trb = {0};
+
+ _xusb_create_normal_trb(&trb, buf, len, direction);
+ int ep_idx = USB_EP_BULK_IN;
+ if (direction == USB_DIR_OUT)
+ ep_idx = USB_EP_BULK_OUT;
+ int res = _xusb_queue_trb(ep_idx, &trb, EP_RING_DOORBELL);
+ if (!res)
+ usbd_xotg->wait_for_event_trb = XUSB_TRB_NORMAL;
+
+ return res;
+}
+
+static int _xusb_issue_data_trb(u8 *buf, u32 len, usb_dir_t direction)
+{
+ data_trb_t trb = {0};
+
+ int res = USB_RES_OK;
+ if (usbd_xotg->cntrl_epenqueue_ptr == usbd_xotg->cntrl_epdequeue_ptr)
+ {
+ _xusb_create_data_trb(&trb, buf, len, direction);
+ res = _xusb_queue_trb(XUSB_EP_CTRL_IN, &trb, EP_RING_DOORBELL);
+ if (!res)
+ usbd_xotg->wait_for_event_trb = XUSB_TRB_DATA;
+ }
+ return res;
+}
+
+int xusb_set_ep_stall(u32 endpoint, int ep_stall)
+{
+ int ep_idx = BIT(endpoint);
+ if (ep_stall)
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_HALT) |= ep_idx;
+ else
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_HALT) &= ~ep_idx;
+
+ // Wait for EP status to change.
+ int res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_EP_STCHG, ep_idx, ep_idx, 1000);
+ if (res)
+ return res;
+
+ // Clear status change.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_STCHG) = ep_idx;
+
+ return USB_RES_OK;
+}
+
+static int _xusb_handle_transfer_event(transfer_event_trb_t *trb)
+{
+ // Advance dequeue list.
+ data_trb_t *next_trb;
+ switch (trb->ep_id)
+ {
+ case XUSB_EP_CTRL_IN:
+ next_trb = &usbd_xotg->cntrl_epdequeue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ next_trb = (data_trb_t *)(next_trb->databufptr_lo & 0xFFFFFFF0);
+ usbd_xotg->cntrl_epdequeue_ptr = next_trb;
+ break;
+ case USB_EP_BULK_OUT:
+ next_trb = &usbd_xotg->bulkout_epdequeue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ next_trb = (data_trb_t *)(next_trb->databufptr_lo & 0xFFFFFFF0);
+ usbd_xotg->bulkout_epdequeue_ptr = next_trb;
+ break;
+ case USB_EP_BULK_IN:
+ next_trb = &usbd_xotg->bulkin_epdequeue_ptr[1];
+ if (next_trb->trb_type == XUSB_TRB_LINK)
+ next_trb = (data_trb_t *)(next_trb->databufptr_lo & 0xFFFFFFF0);
+ usbd_xotg->bulkin_epdequeue_ptr = next_trb;
+ break;
+ default:
+ // Should never happen.
+ break;
+ }
+
+ // Handle completion code.
+ switch (trb->comp_code)
+ {
+ case XUSB_COMP_SUCCESS:
+ case XUSB_COMP_SHORT_PKT:
+ switch (trb->ep_id)
+ {
+ case XUSB_EP_CTRL_IN:
+ if (usbd_xotg->wait_for_event_trb == XUSB_TRB_DATA)
+ return _xusb_issue_status_trb(USB_DIR_OUT);
+ else if (usbd_xotg->wait_for_event_trb == XUSB_TRB_STATUS)
+ {
+ if (usbd_xotg->device_state == XUSB_ADDRESSED_STS_WAIT)
+ usbd_xotg->device_state = XUSB_ADDRESSED;
+ else if (usbd_xotg->device_state == XUSB_CONFIGURED_STS_WAIT)
+ usbd_xotg->device_state = XUSB_CONFIGURED;
+ else if (usbd_xotg->device_state == XUSB_LUN_CONFIGURED_STS_WAIT)
+ usbd_xotg->device_state = XUSB_LUN_CONFIGURED;
+ else if (usbd_xotg->device_state == XUSB_HID_CONFIGURED_STS_WAIT)
+ usbd_xotg->device_state = XUSB_HID_CONFIGURED;
+ }
+ break;
+
+ case USB_EP_BULK_IN:
+ usbd_xotg->bytes_remaining[USB_DIR_IN] -= trb->trb_tx_len;
+ if (usbd_xotg->tx_count[USB_DIR_IN])///////////
+ usbd_xotg->tx_count[USB_DIR_IN]--;
+
+ // If bytes remaining for a Bulk IN transfer, return error.
+ if (trb->trb_tx_len)
+ return XUSB_ERROR_XFER_BULK_IN_RESIDUE;
+ break;
+
+ case USB_EP_BULK_OUT:
+ // If short packet and Bulk OUT, it's not an error because we prime EP for 4KB.
+ usbd_xotg->bytes_remaining[USB_DIR_OUT] -= trb->trb_tx_len;
+ if (usbd_xotg->tx_count[USB_DIR_OUT])///////////
+ usbd_xotg->tx_count[USB_DIR_OUT]--;
+ break;
+ }
+ return USB_RES_OK;
+/*
+ case XUSB_COMP_USB_TRANSACTION_ERROR:
+ case XUSB_COMP_TRB_ERROR:
+ case XUSB_COMP_RING_UNDERRUN:
+ case XUSB_COMP_RING_OVERRUN:
+ case XUSB_COMP_CTRL_DIR_ERROR: // Redefined.
+ xusb_set_ep_stall(trb->ep_id, USB_EP_CFG_STALL);
+ return USB_RES_OK;
+*/
+ case XUSB_COMP_CTRL_DIR_ERROR:
+ return XUSB_ERROR_XFER_DIR;
+
+ case XUSB_COMP_CTRL_SEQ_NUM_ERROR:
+ return XUSB_ERROR_SEQ_NUM; //! TODO: Can mean a new setup packet was received.
+
+ default: // Every other completion code.
+ return USB_ERROR_XFER_ERROR;
+ }
+}
+
+/*
+ * Other XUSB impl:
+ * CBT: PR, PRC, WPR, WRC, CSC, REQ, PLC, CEC.
+ * LNX: REQ, PRC PR, PRC & !PR, WRC, CSC, PLC, CEC.
+ * BRO: CSC, PR | PRC, WPR | WRC, REQ, PLC, CEC.
+ */
+
+static int _xusb_handle_port_change()
+{
+ u32 res = USB_RES_OK;
+ u32 status = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC);
+ u32 halt = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT);
+
+ // Connect status change (CSC).
+ if (status & XHCI_PORTSC_CSC)
+ {
+ //! TODO: Check CCS.
+ // CCS check seems to be
+ // XHCI_PORTSC_CCS 1: device_state = XUSB_CONNECTED
+ // XHCI_PORTSC_CCS 0: device_state = XUSB_DISCONNECTED
+ // Always do XHCI_PORTSC_CSC bit clear.
+
+ // Set port speed.
+ usbd_xotg->port_speed = (status & XHCI_PORTSC_PS) >> 10;
+
+ // In case host does not support Super Speed, revert the control EP packet size.
+ if (usbd_xotg->port_speed != XUSB_SUPER_SPEED)
+ {
+ volatile xusb_ep_ctx_t *ep_ctxt = &xusb_evtq->xusb_ep_ctxt[XUSB_EP_CTRL_IN];
+ ep_ctxt->avg_trb_len = 8;
+ ep_ctxt->max_packet_size = 64;
+ }
+
+ // Clear CSC bit.
+ status |= XHCI_PORTSC_CSC;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) = status;
+ }
+
+ // Port reset (PR), Port reset change (PRC).
+ if (status & XHCI_PORTSC_PR || status & XHCI_PORTSC_PRC)
+ {
+ //! TODO:
+ // XHCI_PORTSC_PR: device_state = XUSB_RESET
+
+ //_disable_usb_wdt4();
+
+ //res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_PORTSC, XHCI_PORTSC_PRC, XHCI_PORTSC_PRC, 50000); // unpatched0
+ // if (res) return res;
+ _xusb_xhci_mask_wait(XUSB_DEV_XHCI_PORTSC, XHCI_PORTSC_PRC, XHCI_PORTSC_PRC, 50000); // patched0
+
+ // Clear PRC bit.
+ status = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) | XHCI_PORTSC_PRC;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) |= XHCI_PORTSC_PRC;
+ }
+
+ // Warm Port Reset (WPR), Warm Port Reset Change (WRC).
+ if (status & XHCI_PORTSC_WPR || status & XHCI_PORTSC_WRC)
+ {
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT) &= ~XHCI_PORTHALT_HALT_LTSSM;
+ (void)XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC);
+ res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_PORTSC, XHCI_PORTSC_WRC, XHCI_PORTSC_WRC, 1000);
+
+ // Clear WRC bit.
+ status = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) | XHCI_PORTSC_WRC;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) |= XHCI_PORTSC_WRC;
+
+ //! TODO: WPR: device_state = XUSB_RESET
+ }
+
+ // Handle Config Request (STCHG_REQ).
+ if (halt & XHCI_PORTHALT_STCHG_REQ)
+ {
+ // Clear Link Training Status.
+ status = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT) & ~XHCI_PORTHALT_HALT_LTSSM;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT) &= ~XHCI_PORTHALT_HALT_LTSSM;
+ }
+
+ // Port link state change (PLC).
+ if (status & XHCI_PORTSC_PLC)
+ {
+ //! WAR: Sometimes port speed changes without a CSC event. Set again.
+ usbd_xotg->port_speed = (status & XHCI_PORTSC_PS) >> 10;
+
+ // check PLS
+ // if U3
+ // device_state = XUSB_SUSPENDED
+ // else if U0 and XUSB_SUSPENDED
+ // val = XUSB_DEV_XHCI_EP_PAUSE
+ // XUSB_DEV_XHCI_EP_PAUSE = 0
+ // XUSB_DEV_XHCI_EP_STCHG = val;
+
+ // Clear PLC bit.
+ status = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) | XHCI_PORTSC_PLC;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) |= XHCI_PORTSC_PLC;
+ }
+
+ // Port configuration link error (CEC).
+ if (status & XHCI_PORTSC_CEC)
+ {
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) |= XHCI_PORTSC_CEC;
+ res = XUSB_ERROR_PORT_CFG;
+ }
+
+ return res;
+}
+
+static int _xusb_handle_get_ep_status(usb_ctrl_setup_t *ctrl_setup)
+{
+ static u8 xusb_ep_status_descriptor[2] = {0};
+
+ // Get EP context pointer.
+ volatile xusb_ep_ctx_t *ep_ctxt = (volatile xusb_ep_ctx_t *)(XUSB_DEV_XHCI(XUSB_DEV_XHCI_ECPLO) & 0xFFFFFFF0);
+ ep_ctxt = &ep_ctxt[ctrl_setup->wIndex];
+
+ xusb_ep_status_descriptor[0] = (ep_ctxt->ep_state == EP_HALTED) ? USB_STATUS_EP_HALTED : USB_STATUS_EP_OK;
+ return _xusb_issue_data_trb(xusb_ep_status_descriptor, 2, USB_DIR_IN);
+}
+
+static int _xusb_handle_get_class_request(usb_ctrl_setup_t *ctrl_setup)
+{
+ u8 _bRequest = ctrl_setup->bRequest;
+ u16 _wIndex = ctrl_setup->wIndex;
+ u16 _wValue = ctrl_setup->wValue;
+ u16 _wLength = ctrl_setup->wLength;
+
+ bool valid_interface = _wIndex == usbd_xotg->interface_num;
+ bool valid_len = (_bRequest == USB_REQUEST_BULK_GET_MAX_LUN) ? 1 : 0;
+
+ if (!valid_interface || _wValue != 0 || _wLength != valid_len)
+ goto stall;
+
+ switch (_bRequest)
+ {
+ case USB_REQUEST_BULK_RESET:
+ usbd_xotg->bulk_reset_req = true;
+ return _xusb_issue_status_trb(USB_DIR_IN); // DELAYED_STATUS;
+ case USB_REQUEST_BULK_GET_MAX_LUN:
+ if (!usbd_xotg->max_lun_set)
+ goto stall;
+ usbd_xotg->device_state = XUSB_LUN_CONFIGURED_STS_WAIT;
+ return _xusb_issue_data_trb(&usbd_xotg->max_lun, 1, USB_DIR_IN);
+ }
+
+stall:
+ xusb_set_ep_stall(XUSB_EP_CTRL_IN, USB_EP_CFG_STALL);
+ return USB_RES_OK;
+}
+
+static int _xusb_handle_get_descriptor(usb_ctrl_setup_t *ctrl_setup)
+{
+ u32 size;
+ void *descriptor;
+
+ u32 wLength = ctrl_setup->wLength;
+
+ u8 descriptor_type = ctrl_setup->wValue >> 8;
+ u8 descriptor_subtype = ctrl_setup->wValue & 0xFF;
+
+ switch (descriptor_type)
+ {
+ case USB_DESCRIPTOR_DEVICE:
+ //! TODO USB3: Provide a super speed descriptor.
+/*
+ u32 soc_rev = APB_MISC(APB_MISC_GP_HIDREV);
+ usb_device_descriptor.idProduct = (soc_rev >> 8) & 0xFF; // chip_id.
+ usb_device_descriptor.idProduct |= ((soc_rev << 4) | (FUSE(FUSE_SKU_INFO) & 0xF)) << 8; // HIDFAM.
+ usb_device_descriptor.bcdDevice = (soc_rev >> 16) & 0xF; // MINORREV.
+ usb_device_descriptor.bcdDevice |= ((soc_rev >> 4) & 0xF) << 8; // MAJORREV.
+*/
+ descriptor = usbd_xotg->desc->dev;
+ size = usbd_xotg->desc->dev->bLength;
+ break;
+ case USB_DESCRIPTOR_CONFIGURATION:
+ //! TODO USB3: Provide a super speed descriptor.
+ if (usbd_xotg->gadget == USB_GADGET_UMS)
+ {
+ if (usbd_xotg->port_speed == XUSB_HIGH_SPEED) // High speed. 512 bytes.
+ {
+ usbd_xotg->desc->cfg->endpoint[0].wMaxPacketSize = 0x200; // No burst.
+ usbd_xotg->desc->cfg->endpoint[1].wMaxPacketSize = 0x200; // No burst.
+ }
+ else // Full speed. 64 bytes.
+ {
+ usbd_xotg->desc->cfg->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_xotg->desc->cfg->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ }
+ else
+ {
+ usb_cfg_hid_descr_t *tmp = (usb_cfg_hid_descr_t *)usbd_xotg->desc->cfg;
+ if (usbd_xotg->port_speed == XUSB_HIGH_SPEED) // High speed. 512 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x200;
+ tmp->endpoint[1].wMaxPacketSize = 0x200;
+ tmp->endpoint[0].bInterval = usbd_xotg->gadget == USB_GADGET_HID_GAMEPAD ? 4 : 3; // 8ms : 4ms.
+ tmp->endpoint[1].bInterval = usbd_xotg->gadget == USB_GADGET_HID_GAMEPAD ? 4 : 3; // 8ms : 4ms.
+ }
+ else // Full speed. 64 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x40;
+ tmp->endpoint[1].wMaxPacketSize = 0x40;
+ tmp->endpoint[0].bInterval = usbd_xotg->gadget == USB_GADGET_HID_GAMEPAD ? 8 : 4; // 8ms : 4ms.
+ tmp->endpoint[1].bInterval = usbd_xotg->gadget == USB_GADGET_HID_GAMEPAD ? 8 : 4; // 8ms : 4ms.
+ }
+ }
+ descriptor = usbd_xotg->desc->cfg;
+ size = usbd_xotg->desc->cfg->config.wTotalLength;
+ break;
+ case USB_DESCRIPTOR_STRING:
+ switch (descriptor_subtype)
+ {
+ case 1:
+ descriptor = usbd_xotg->desc->vendor;
+ size = usbd_xotg->desc->vendor[0];
+ break;
+ case 2:
+ descriptor = usbd_xotg->desc->product;
+ size = usbd_xotg->desc->product[0];
+ break;
+ case 3:
+ descriptor = usbd_xotg->desc->serial;
+ size = usbd_xotg->desc->serial[0];
+ break;
+ case 0xEE:
+ descriptor = usbd_xotg->desc->ms_os;
+ size = usbd_xotg->desc->ms_os->bLength;
+ break;
+ default:
+ descriptor = usbd_xotg->desc->lang_id;
+ size = 4;
+ break;
+ }
+ break;
+ case USB_DESCRIPTOR_DEVICE_QUALIFIER:
+ if (!usbd_xotg->desc->dev_qual)
+ {
+ xusb_set_ep_stall(XUSB_EP_CTRL_IN, USB_EP_CFG_STALL);
+ return USB_RES_OK;
+ }
+ usbd_xotg->desc->dev_qual->bNumOtherConfigs = 0;
+ descriptor = usbd_xotg->desc->dev_qual;
+ size = usbd_xotg->desc->dev_qual->bLength;
+ break;
+ case USB_DESCRIPTOR_OTHER_SPEED_CONFIGURATION:
+ if (!usbd_xotg->desc->cfg_other)
+ {
+ xusb_set_ep_stall(XUSB_EP_CTRL_IN, USB_EP_CFG_STALL);
+ return USB_RES_OK;
+ }
+ if (usbd_xotg->port_speed == XUSB_HIGH_SPEED)
+ {
+ usbd_xotg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_xotg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ else
+ {
+ usbd_xotg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x200;
+ usbd_xotg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ descriptor = usbd_xotg->desc->cfg_other;
+ size = usbd_xotg->desc->cfg_other->config.wTotalLength;
+ break;
+ case USB_DESCRIPTOR_DEVICE_BINARY_OBJECT:
+ descriptor = usbd_xotg->desc->dev_bot;
+ size = usbd_xotg->desc->dev_bot->wTotalLength;
+ break;
+ default:
+ xusb_set_ep_stall(XUSB_EP_CTRL_IN, USB_EP_CFG_STALL);
+ return USB_RES_OK;
+ }
+
+ if (wLength < size)
+ size = wLength;
+
+ return _xusb_issue_data_trb(descriptor, size, USB_DIR_IN);
+}
+
+static void _xusb_handle_set_request_dev_address(usb_ctrl_setup_t *ctrl_setup)
+{
+ u32 addr = ctrl_setup->wValue & 0xFF;
+
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CTRL) = (XUSB_DEV_XHCI(XUSB_DEV_XHCI_CTRL) & 0x80FFFFFF) | (addr << 24);
+ xusb_evtq->xusb_ep_ctxt[XUSB_EP_CTRL_IN].device_addr = addr;
+
+ _xusb_issue_status_trb(USB_DIR_IN);
+
+ usbd_xotg->device_state = XUSB_ADDRESSED_STS_WAIT;
+}
+
+static void _xusb_handle_set_request_configuration(usb_ctrl_setup_t *ctrl_setup)
+{
+ u32 config_num = ctrl_setup->wValue;
+ if (!config_num) //TODO! we can change device_state here.
+ return;
+
+ // Initialize BULK EPs.
+ _xusbd_ep_initialize(USB_EP_BULK_OUT);
+ _xusbd_ep_initialize(USB_EP_BULK_IN);
+
+ // Device mode start.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CTRL) |= XHCI_CTRL_RUN;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ST) |= XHCI_ST_RC;
+
+ _xusb_issue_status_trb(USB_DIR_IN);
+
+ usbd_xotg->config_num = config_num;
+ usbd_xotg->device_state = XUSB_CONFIGURED_STS_WAIT;
+}
+
+static int _xusbd_handle_ep0_control_transfer(usb_ctrl_setup_t *ctrl_setup)
+{
+ u32 size;
+ u8 *desc;
+ bool ep_stall = false;
+ bool transmit_data = false;
+
+ u8 _bmRequestType = ctrl_setup->bmRequestType;
+ u8 _bRequest = ctrl_setup->bRequest;
+ u16 _wValue = ctrl_setup->wValue;
+ u16 _wIndex = ctrl_setup->wIndex;
+ u16 _wLength = ctrl_setup->wLength;
+
+ static u8 xusb_dev_status_descriptor[2] = {USB_STATUS_DEV_SELF_POWERED, 0};
+ static u8 xusb_interface_descriptor[4] = {0};
+ static u8 xusb_configuration_descriptor[2] = {0};
+ static u8 xusb_status_descriptor[2] = {0};
+
+ //gfx_printf("ctrl: %02X %02X %04X %04X %04X\n", _bmRequestType, _bRequest, _wValue, _wIndex, _wLength);
+
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_EP_HALT) &= ~XHCI_EP_HALT_DCI;
+ u32 res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_EP_HALT, XHCI_EP_HALT_DCI, 0, 1000);
+ if (res)
+ return res;
+
+ switch (_bmRequestType)
+ {
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE):
+ if (_bRequest == USB_REQUEST_SET_ADDRESS)
+ _xusb_handle_set_request_dev_address(ctrl_setup);
+ else if (_bRequest == USB_REQUEST_SET_CONFIGURATION)
+ _xusb_handle_set_request_configuration(ctrl_setup);
+ return USB_RES_OK; // What about others.
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE):
+ usbd_xotg->interface_num = _wValue;
+ return _xusb_issue_status_trb(USB_DIR_IN);
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT):
+ if ((_wValue & 0xFF) == USB_FEATURE_ENDPOINT_HALT)
+ {
+ if (_bRequest == USB_REQUEST_CLEAR_FEATURE)
+ {
+ xusb_set_ep_stall(_wIndex, USB_EP_CFG_CLEAR);
+ return _xusb_issue_status_trb(USB_DIR_IN);
+ }
+ else if (_bRequest == USB_REQUEST_SET_FEATURE)
+ {
+ xusb_set_ep_stall(_wIndex, USB_EP_CFG_STALL);
+ return _xusb_issue_status_trb(USB_DIR_IN);
+ }
+ }
+ ep_stall = true;
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE):
+ return _xusb_handle_get_class_request(ctrl_setup);
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE):
+ switch (_bRequest)
+ {
+ case USB_REQUEST_GET_STATUS:
+ desc = xusb_dev_status_descriptor;
+ size = sizeof(xusb_dev_status_descriptor);
+ transmit_data = true;
+ break;
+ case USB_REQUEST_GET_DESCRIPTOR:
+ return _xusb_handle_get_descriptor(ctrl_setup);
+ case USB_REQUEST_GET_CONFIGURATION:
+ xusb_configuration_descriptor[0] = usbd_xotg->config_num;
+ desc = xusb_configuration_descriptor;
+ size = sizeof(xusb_configuration_descriptor);
+ transmit_data = true;
+ break;
+ default:
+ ep_stall = true;
+ break;
+ }
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE):
+ if (_bRequest == USB_REQUEST_GET_INTERFACE)
+ {
+ desc = xusb_interface_descriptor;
+ size = sizeof(xusb_interface_descriptor);
+ xusb_interface_descriptor[0] = usbd_xotg->interface_num;
+ transmit_data = true;
+ }
+ else if (_bRequest == USB_REQUEST_GET_STATUS)
+ {
+ desc = xusb_status_descriptor;
+ size = sizeof(xusb_status_descriptor);
+ transmit_data = true;
+ }
+ else if (_bRequest == USB_REQUEST_GET_DESCRIPTOR && (_wValue >> 8) == USB_DESCRIPTOR_HID_REPORT && usbd_xotg->gadget > USB_GADGET_UMS)
+ {
+ if (usbd_xotg->gadget == USB_GADGET_HID_GAMEPAD)
+ {
+ desc = (u8 *)&hid_report_descriptor_jc;
+ size = hid_report_descriptor_jc_size;
+ }
+ else // USB_GADGET_HID_TOUCHPAD
+ {
+ desc = (u8 *)&hid_report_descriptor_touch;
+ size = hid_report_descriptor_touch_size;
+ }
+ transmit_data = true;
+ usbd_xotg->device_state = XUSB_HID_CONFIGURED_STS_WAIT;
+ }
+ else
+ ep_stall = true;
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT):
+ if (_bRequest == USB_REQUEST_GET_STATUS)
+ return _xusb_handle_get_ep_status(ctrl_setup);
+
+ ep_stall = true;
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE):
+ return _xusb_handle_get_class_request(ctrl_setup);
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_INTERFACE):
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE):
+ if (_bRequest == USB_REQUEST_GET_MS_DESCRIPTOR)
+ {
+ switch (_wIndex)
+ {
+ case USB_DESCRIPTOR_MS_COMPAT_ID:
+ desc = (u8 *)usbd_xotg->desc->ms_cid;
+ size = usbd_xotg->desc->ms_cid->dLength;
+ transmit_data = true;
+ break;
+ case USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES:
+ desc = (u8 *)usbd_xotg->desc->mx_ext;
+ size = usbd_xotg->desc->mx_ext->dLength;
+ transmit_data = true;
+ break;
+ default:
+ ep_stall = true;
+ break;
+ }
+ }
+ else
+ ep_stall = true;
+ break;
+
+ default:
+ ep_stall = true;
+ break;
+ }
+
+ if (transmit_data)
+ {
+ memcpy((u8 *)USB_EP_CONTROL_BUF_ADDR, desc, size);
+ if (_wLength < size)
+ size = _wLength;
+ return _xusb_issue_data_trb((u8 *)USB_EP_CONTROL_BUF_ADDR, size, USB_DIR_IN);
+ }
+
+ if (ep_stall)
+ xusb_set_ep_stall(XUSB_EP_CTRL_IN, USB_EP_CFG_STALL);
+
+ return USB_RES_OK;
+}
+
+static int _xusb_ep_operation(u32 tries)
+{
+ usb_ctrl_setup_t setup_event;
+ volatile event_trb_t *event_trb;
+ setup_event_trb_t *setup_event_trb;
+
+ // Wait for an interrupt event.
+ int res = _xusb_xhci_mask_wait(XUSB_DEV_XHCI_ST, XHCI_ST_IP, XHCI_ST_IP, tries);
+ if (res)
+ return res;
+
+ // Clear interrupt status.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ST) |= XHCI_ST_IP;
+
+ usbd_xotg->event_enqueue_ptr = (event_trb_t *)(XUSB_DEV_XHCI(XUSB_DEV_XHCI_EREPLO) & 0xFFFFFFF0);
+ event_trb = usbd_xotg->event_dequeue_ptr;
+
+ // Check if cycle matches.
+ if ((event_trb->cycle & 1) != usbd_xotg->event_ccs)
+ return XUSB_ERROR_INVALID_CYCLE;
+
+ while ((event_trb->cycle & 1) == usbd_xotg->event_ccs)
+ {
+ switch (event_trb->trb_type)
+ {
+ case XUSB_TRB_TRANSFER:
+ res = _xusb_handle_transfer_event((transfer_event_trb_t *)event_trb);
+ break;
+ case XUSB_TRB_PORT_CHANGE:
+ res = _xusb_handle_port_change();
+ break;
+ case XUSB_TRB_SETUP:
+ setup_event_trb = (setup_event_trb_t *)event_trb;
+ memcpy(&setup_event, &setup_event_trb->ctrl_setup_data, sizeof(usb_ctrl_setup_t));
+ usbd_xotg->ctrl_seq_num = setup_event_trb->ctrl_seq_num;
+ res = _xusbd_handle_ep0_control_transfer(&setup_event);
+ break;
+ default:
+ // TRB not supported.
+ break;
+ }
+
+ // Check if last event TRB and reset to first one.
+ if (usbd_xotg->event_dequeue_ptr == &xusb_evtq->xusb_event_ring_seg1[XUSB_LAST_TRB_IDX])
+ {
+ usbd_xotg->event_dequeue_ptr = xusb_evtq->xusb_event_ring_seg0;
+ usbd_xotg->event_ccs ^= 1;
+ }
+ else // Advance dequeue to next event.
+ usbd_xotg->event_dequeue_ptr = &usbd_xotg->event_dequeue_ptr[1];
+
+ // Set next event.
+ event_trb = usbd_xotg->event_dequeue_ptr;
+
+ // If events exceed the interrupt time, handle them next interrupt.
+ if (usbd_xotg->event_dequeue_ptr == usbd_xotg->event_enqueue_ptr)
+ break;
+ }
+
+ // Clear Event Handler bit if enabled and set Dequeue pointer.
+ u32 erdp = XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERDPLO) & 0xF;
+ if (erdp & XHCI_ERDPLO_EHB)
+ erdp |= XHCI_ERDPLO_EHB;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_ERDPLO) = ((u32)usbd_xotg->event_dequeue_ptr & 0xFFFFFFF0) | erdp;
+
+ return res;
+}
+
+int xusb_device_enumerate(usb_gadget_type gadget)
+{
+ switch (gadget)
+ {
+ case USB_GADGET_UMS:
+ usbd_xotg->desc = &usb_gadget_ums_descriptors;
+ break;
+ case USB_GADGET_HID_GAMEPAD:
+ usbd_xotg->desc = &usb_gadget_hid_jc_descriptors;
+ break;
+ case USB_GADGET_HID_TOUCHPAD:
+ usbd_xotg->desc = &usb_gadget_hid_touch_descriptors;
+ break;
+ }
+
+ usbd_xotg->gadget = gadget;
+
+ /*
+ * Set interrupt moderation to 0us.
+ * This is important because default value creates a 4.62ms latency.
+ * Effectively hurting transfers by having 15% to 96% performance loss.
+ */
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_RT_IMOD) = 0;
+
+ // Disable Wake events.
+ XUSB_PADCTL(XUSB_PADCTL_ELPG_PROGRAM_0) = 0;
+ XUSB_PADCTL(XUSB_PADCTL_ELPG_PROGRAM_1) = 0;
+
+ // Enable overrides for VBUS and ID.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_VBUS_ID) = (XUSB_PADCTL(XUSB_PADCTL_USB2_VBUS_ID) & ~(PADCTL_USB2_VBUS_ID_VBUS_OVR_MASK | PADCTL_USB2_VBUS_ID_SRC_MASK)) |
+ PADCTL_USB2_VBUS_ID_VBUS_OVR_EN | PADCTL_USB2_VBUS_ID_SRC_ID_OVR_EN;
+
+ // Clear halt for LTSSM.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTHALT) &= ~XHCI_PORTHALT_HALT_LTSSM;
+
+ // Enable device mode.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CTRL) |= XHCI_CTRL_ENABLE;
+
+ // Override access to High/Full Speed.
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CFG_DEV_FE) = (XUSB_DEV_XHCI(XUSB_DEV_XHCI_CFG_DEV_FE) & ~XHCI_CFG_DEV_FE_PORTREGSEL_MASK) | XHCI_CFG_DEV_FE_PORTREGSEL_HSFS;
+
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) =
+ (XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) & ~XHCI_PORTSC_PLS_MASK) | XHCI_PORTSC_LWS | XHCI_PORTSC_PLS_RXDETECT;
+ XUSB_DEV_XHCI(XUSB_DEV_XHCI_CFG_DEV_FE) &= ~XHCI_CFG_DEV_FE_PORTREGSEL_MASK;
+
+ // Enable VBUS and set ID to Float.
+ XUSB_PADCTL(XUSB_PADCTL_USB2_VBUS_ID) = (XUSB_PADCTL(XUSB_PADCTL_USB2_VBUS_ID) & ~PADCTL_USB2_VBUS_ID_OVR_MASK) |
+ PADCTL_USB2_VBUS_ID_OVR_FLOAT | PADCTL_USB2_VBUS_ID_VBUS_ON;
+
+ usbd_xotg->wait_for_event_trb = XUSB_TRB_SETUP;
+ usbd_xotg->device_state = XUSB_DEFAULT;
+
+ // Timeout if cable or communication isn't started in 1.5 minutes.
+ u32 timer = get_tmr_ms() + 90000;
+ while (true)
+ {
+ int res = _xusb_ep_operation(USB_XFER_SYNCED_ENUM); // 2s timeout.
+ if (res && res != USB_ERROR_TIMEOUT)
+ return res;
+
+ if (usbd_xotg->device_state == XUSB_CONFIGURED)
+ break;
+
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return USB_ERROR_USER_ABORT;
+ }
+
+ return USB_RES_OK;
+}
+
+void xusb_end(bool reset_ep, bool only_controller)
+{
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB_SS);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_CLR) = BIT(CLK_W_XUSB_SS);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_SET) = BIT(CLK_U_XUSB_DEV);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_CLR) = BIT(CLK_U_XUSB_DEV);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB_PADCTL);
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_CLR) = BIT(CLK_W_XUSB);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB);
+ mc_disable_ahb_redirect(); // Can be skipped if IRAM is not used.
+}
+
+int xusb_handle_ep0_ctrl_setup()
+{
+ /*
+ * EP0 Control handling is done by normal ep operation in XUSB.
+ * Here we handle the bulk reset only.
+ */
+ if (usbd_xotg->bulk_reset_req)
+ {
+ usbd_xotg->bulk_reset_req = false;
+ return USB_RES_BULK_RESET;
+ }
+
+ return USB_RES_OK;
+}
+
+int xusb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, u32 sync_tries)
+{
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ int res = USB_RES_OK;
+ usbd_xotg->tx_count[USB_DIR_OUT] = 0;
+ usbd_xotg->bytes_remaining[USB_DIR_OUT] = len;
+ _xusb_issue_normal_trb(buf, len, USB_DIR_OUT);
+ usbd_xotg->tx_count[USB_DIR_OUT]++;
+
+ if (sync_tries)
+ {
+ while (!res && usbd_xotg->tx_count[USB_DIR_OUT])
+ res = _xusb_ep_operation(sync_tries);
+
+ if (bytes_read)
+ *bytes_read = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_OUT];
+
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+ }
+
+ return res;
+}
+
+int xusb_device_ep1_out_read_big(u8 *buf, u32 len, u32 *bytes_read)
+{
+ if (len > USB_EP_BULK_OUT_MAX_XFER)
+ len = USB_EP_BULK_OUT_MAX_XFER;
+
+ u32 bytes = 0;
+ *bytes_read = 0;
+ u8 *buf_curr = buf;
+
+ while (len)
+ {
+ u32 len_ep = MIN(len, USB_EP_BUFFER_MAX_SIZE);
+
+ int res = xusb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED_DATA);
+ if (res)
+ return res;
+
+ len -= len_ep;
+ buf_curr += len_ep;
+ *bytes_read = *bytes_read + bytes;
+ }
+
+ return USB_RES_OK;
+}
+
+int xusb_device_ep1_out_reading_finish(u32 *pending_bytes)
+{
+ int res = USB_RES_OK;
+ while (!res && usbd_xotg->tx_count[USB_DIR_OUT])
+ res = _xusb_ep_operation(USB_XFER_SYNCED); // Infinite retries.
+
+ if (pending_bytes)
+ *pending_bytes = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_OUT];
+
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+
+ return res;
+}
+
+int xusb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, u32 sync_tries)
+{
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+
+ int res = USB_RES_OK;
+ usbd_xotg->tx_count[USB_DIR_IN] = 0;
+ usbd_xotg->bytes_remaining[USB_DIR_IN] = len;
+ _xusb_issue_normal_trb(buf, len, USB_DIR_IN);
+ usbd_xotg->tx_count[USB_DIR_IN]++;
+
+ if (sync_tries)
+ {
+ while (!res && usbd_xotg->tx_count[USB_DIR_IN])
+ res = _xusb_ep_operation(sync_tries);
+
+ if (bytes_written)
+ *bytes_written = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_IN];
+ }
+ else
+ {
+ if ((usbd_xotg->port_speed == XUSB_FULL_SPEED && len == 64) ||
+ (usbd_xotg->port_speed == XUSB_HIGH_SPEED && len == 512) ||
+ (usbd_xotg->port_speed == XUSB_SUPER_SPEED && len == 1024))
+ {
+ _xusb_issue_normal_trb(buf, 0, USB_DIR_IN);
+ usbd_xotg->tx_count[USB_DIR_IN]++;
+ }
+ }
+
+ return res;
+}
+
+int xusb_device_ep1_in_writing_finish(u32 *pending_bytes)
+{
+ int res = USB_RES_OK;
+ while (!res && usbd_xotg->tx_count[USB_DIR_IN])
+ res = _xusb_ep_operation(USB_XFER_SYNCED); // Infinite retries.
+
+ if (pending_bytes)
+ *pending_bytes = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_IN];
+
+ return res;
+}
+
+bool xusb_device_get_port_in_sleep()
+{
+ // Ejection heuristic.
+ u32 link_mode = XUSB_DEV_XHCI(XUSB_DEV_XHCI_PORTSC) & XHCI_PORTSC_PLS_MASK;
+ return (link_mode == XHCI_PORTSC_PLS_U3);
+}
+
+bool xusb_device_class_send_max_lun(u8 max_lun)
+{
+ // Timeout if get MAX_LUN request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ usbd_xotg->max_lun = max_lun;
+ usbd_xotg->max_lun_set = true;
+
+ // Wait for request and transfer start.
+ while (usbd_xotg->device_state != XUSB_LUN_CONFIGURED)
+ {
+ _xusb_ep_operation(USB_XFER_SYNCED_CLASS);
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return true;
+ }
+
+ usbd_xotg->device_state = XUSB_CONFIGURED;
+
+ return false;
+}
+
+bool xusb_device_class_send_hid_report()
+{
+ // Timeout if get GET_HID_REPORT request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ // Wait for request and transfer start.
+ while (usbd_xotg->device_state != XUSB_HID_CONFIGURED)
+ {
+ _xusb_ep_operation(USB_XFER_SYNCED_CLASS);
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return true;
+ }
+
+ usbd_xotg->device_state = XUSB_CONFIGURED;
+
+ return false;
+}
+
+void xusb_device_get_ops(usb_ops_t *ops)
+{
+ ops->usbd_flush_endpoint = NULL;
+ ops->usbd_set_ep_stall = xusb_set_ep_stall;
+ ops->usbd_handle_ep0_ctrl_setup = xusb_handle_ep0_ctrl_setup;
+ ops->usbd_end = xusb_end;//////////////////
+ ops->usb_device_init = xusb_device_init;
+ ops->usb_device_enumerate = xusb_device_enumerate;
+ ops->usb_device_class_send_max_lun = xusb_device_class_send_max_lun;
+ ops->usb_device_class_send_hid_report = xusb_device_class_send_hid_report;
+ ops->usb_device_get_suspended = xusb_device_get_port_in_sleep;
+ ops->usb_device_get_port_in_sleep = xusb_device_get_port_in_sleep;
+
+ ops->usb_device_ep1_out_read = xusb_device_ep1_out_read;
+ ops->usb_device_ep1_out_read_big = xusb_device_ep1_out_read_big;
+ ops->usb_device_ep1_out_reading_finish = xusb_device_ep1_out_reading_finish;
+ ops->usb_device_ep1_in_write = xusb_device_ep1_in_write;
+ ops->usb_device_ep1_in_writing_finish = xusb_device_ep1_in_writing_finish;
+}
diff --git a/bdk/utils/aarch64_util.h b/bdk/utils/aarch64_util.h
new file mode 100644
index 00000000..456bfc83
--- /dev/null
+++ b/bdk/utils/aarch64_util.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _ARM64_H_
+#define _ARM64_H_
+
+#include
+
+#define LSL0 0
+#define LSL16 16
+#define LSL32 32
+
+#define _PAGEOFF(x) ((x) & 0xFFFFF000)
+
+#define _ADRP(r, o) (0x90000000 | ((((o) >> 12) & 0x3) << 29) | ((((o) >> 12) & 0x1FFFFC) << 3) | ((r) & 0x1F))
+#define _BL(a, o) (0x94000000 | ((((o) - (a)) >> 2) & 0x3FFFFFF))
+#define _B(a, o) (0x14000000 | ((((o) - (a)) >> 2) & 0x3FFFFFF))
+#define _MOVKX(r, i, s) (0xF2800000 | (((s) & 0x30) << 17) | (((i) & 0xFFFF) << 5) | ((r) & 0x1F))
+#define _MOVZX(r, i, s) (0xD2800000 | (((s) & 0x30) << 17) | (((i) & 0xFFFF) << 5) | ((r) & 0x1F))
+#define _MOVZW(r, i, s) (0x52800000 | (((s) & 0x30) << 17) | (((i) & 0xFFFF) << 5) | ((r) & 0x1F))
+#define _NOP() 0xD503201F
+
+#endif
diff --git a/bdk/utils/btn.c b/bdk/utils/btn.c
new file mode 100644
index 00000000..cc365734
--- /dev/null
+++ b/bdk/utils/btn.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "btn.h"
+#include
+#include
+#include
+#include
+#include
+
+u8 btn_read()
+{
+ u8 res = 0;
+ if (!gpio_read(GPIO_PORT_X, GPIO_PIN_7))
+ res |= BTN_VOL_DOWN;
+ if (!gpio_read(GPIO_PORT_X, GPIO_PIN_6))
+ res |= BTN_VOL_UP;
+ if (i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFSTAT) & MAX77620_ONOFFSTAT_EN0)
+ res |= BTN_POWER;
+ return res;
+}
+
+u8 btn_read_vol()
+{
+ u8 res = 0;
+ if (!gpio_read(GPIO_PORT_X, GPIO_PIN_7))
+ res |= BTN_VOL_DOWN;
+ if (!gpio_read(GPIO_PORT_X, GPIO_PIN_6))
+ res |= BTN_VOL_UP;
+ return res;
+}
+
+u8 btn_wait()
+{
+ u8 res = 0, btn = btn_read();
+ bool pwr = false;
+
+ //Power button down, raise a filter.
+ if (btn & BTN_POWER)
+ {
+ pwr = true;
+ btn &= ~BTN_POWER;
+ }
+
+ do
+ {
+ res = btn_read();
+ //Power button up, remove filter.
+ if (!(res & BTN_POWER) && pwr)
+ pwr = false;
+ else if (pwr) //Power button still down.
+ res &= ~BTN_POWER;
+ } while (btn == res);
+
+ return res;
+}
+
+u8 btn_wait_timeout(u32 time_ms, u8 mask)
+{
+ u32 timeout = get_tmr_ms() + time_ms;
+ u8 res = btn_read() & mask;
+
+ while (get_tmr_ms() < timeout)
+ {
+ if (res == mask)
+ break;
+ else
+ res = btn_read() & mask;
+ };
+
+ return res;
+}
+
+u8 btn_wait_timeout_single(u32 time_ms, u8 mask)
+{
+ u8 single_button = mask & BTN_SINGLE;
+ mask &= ~BTN_SINGLE;
+
+ u32 timeout = get_tmr_ms() + time_ms;
+ u8 res = btn_read();
+
+ while (get_tmr_ms() < timeout)
+ {
+ if ((res & mask) == mask)
+ {
+ if (single_button && (res & ~mask)) // Undesired button detected.
+ res = btn_read();
+ else
+ return (res & mask);
+ }
+ else
+ res = btn_read();
+ };
+
+ // Timed out.
+ if (!single_button || !time_ms)
+ return (res & mask);
+ else
+ return 0; // Return no button press if single button requested.
+}
diff --git a/bdk/utils/btn.h b/bdk/utils/btn.h
new file mode 100644
index 00000000..ac191fa9
--- /dev/null
+++ b/bdk/utils/btn.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _BTN_H_
+#define _BTN_H_
+
+#include
+
+#define BTN_POWER BIT(0)
+#define BTN_VOL_DOWN BIT(1)
+#define BTN_VOL_UP BIT(2)
+#define BTN_SINGLE BIT(7)
+
+u8 btn_read();
+u8 btn_read_vol();
+u8 btn_wait();
+u8 btn_wait_timeout(u32 time_ms, u8 mask);
+u8 btn_wait_timeout_single(u32 time_ms, u8 mask);
+
+#endif
diff --git a/bdk/utils/dirlist.c b/bdk/utils/dirlist.c
new file mode 100644
index 00000000..e05b2f18
--- /dev/null
+++ b/bdk/utils/dirlist.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 CTCaer
+ * Copyright (c) 2020 Storm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+
+ //Dateien auflisten in Verzeichnis
+char *dirlist(const char *directory, const char *pattern, bool includeHiddenFiles, bool parse_dirs)
+{
+ u8 max_entries = 61;
+
+ int res = 0;
+ u32 i = 0, j = 0, k = 0;
+ DIR dir;
+ FILINFO fno;
+
+ char *dir_entries = (char *)calloc(max_entries, 256);
+ char *temp = (char *)calloc(1, 256);
+
+ if (!pattern && !f_opendir(&dir, directory))
+ {
+ for (;;)
+ {
+ res = f_readdir(&dir, &fno);
+ if (res || !fno.fname[0])
+ break;
+
+ bool curr_parse = parse_dirs ? (fno.fattrib & AM_DIR) : !(fno.fattrib & AM_DIR);
+
+ if (curr_parse)
+ {
+ if ((fno.fname[0] != '.') && (includeHiddenFiles || !(fno.fattrib & AM_HID)))
+ {
+ strcpy(dir_entries + (k * 256), fno.fname);
+ k++;
+ if (k > (max_entries - 1))
+ break;
+ }
+ }
+ }
+ f_closedir(&dir);
+ }
+ else if (pattern && !f_findfirst(&dir, &fno, directory, pattern) && fno.fname[0])
+ {
+ do
+ {
+ if (!(fno.fattrib & AM_DIR) && (fno.fname[0] != '.') && (includeHiddenFiles || !(fno.fattrib & AM_HID)))
+ {
+ strcpy(dir_entries + (k * 256), fno.fname);
+ k++;
+ if (k > (max_entries - 1))
+ break;
+ }
+ res = f_findnext(&dir, &fno);
+ } while (fno.fname[0] && !res);
+ f_closedir(&dir);
+ }
+
+ if (!k)
+ {
+ free(temp);
+ free(dir_entries);
+
+ return NULL;
+ }
+
+ // Reorder ini files by ASCII ordering.
+ for (i = 0; i < k - 1 ; i++)
+ {
+ for (j = i + 1; j < k; j++)
+ {
+ if (strcmp(&dir_entries[i * 256], &dir_entries[j * 256]) > 0)
+ {
+ strcpy(temp, &dir_entries[i * 256]);
+ strcpy(&dir_entries[i * 256], &dir_entries[j * 256]);
+ strcpy(&dir_entries[j * 256], temp);
+ }
+ }
+ }
+
+ free(temp);
+
+ return dir_entries;
+}
diff --git a/bdk/utils/dirlist.h b/bdk/utils/dirlist.h
new file mode 100644
index 00000000..32197f33
--- /dev/null
+++ b/bdk/utils/dirlist.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+char *dirlist(const char *directory, const char *pattern, bool includeHiddenFiles, bool parse_dirs);
diff --git a/bdk/utils/ini.c b/bdk/utils/ini.c
new file mode 100644
index 00000000..538435e1
--- /dev/null
+++ b/bdk/utils/ini.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018-2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "ini.h"
+#include
+#include
+#include
+
+static char *_strdup(char *str)
+{
+ if (!str)
+ return NULL;
+
+ // Remove starting space.
+ if (str[0] == ' ' && strlen(str))
+ str++;
+
+ char *res = (char *)malloc(strlen(str) + 1);
+ strcpy(res, str);
+
+ // Remove trailing space.
+ if (strlen(res) && res[strlen(res) - 1] == ' ')
+ res[strlen(res) - 1] = 0;
+
+ return res;
+}
+
+u32 _find_section_name(char *lbuf, u32 lblen, char schar)
+{
+ u32 i;
+ // Depends on 'FF_USE_STRFUNC 2' that removes \r.
+ for (i = 0; i < lblen && lbuf[i] != schar && lbuf[i] != '\n'; i++)
+ ;
+ lbuf[i] = 0;
+
+ return i;
+}
+
+ini_sec_t *_ini_create_section(link_t *dst, ini_sec_t *csec, char *name, u8 type)
+{
+ if (csec)
+ list_append(dst, &csec->link);
+
+ csec = (ini_sec_t *)calloc(sizeof(ini_sec_t), 1);
+ csec->name = _strdup(name);
+ csec->type = type;
+
+ return csec;
+}
+
+int ini_parse(link_t *dst, char *ini_path, bool is_dir)
+{
+ u32 lblen;
+ u32 pathlen = strlen(ini_path);
+ u32 k = 0;
+ char lbuf[512];
+ char *filelist = NULL;
+ FIL fp;
+ ini_sec_t *csec = NULL;
+
+ char *filename = (char *)malloc(256);
+
+ strcpy(filename, ini_path);
+
+ // Get all ini filenames.
+ if (is_dir)
+ {
+ filelist = dirlist(filename, "*.ini", false, false);
+ if (!filelist)
+ {
+ free(filename);
+ return 0;
+ }
+ strcpy(filename + pathlen, "/");
+ pathlen++;
+ }
+
+ do
+ {
+ // Copy ini filename in path string.
+ if (is_dir)
+ {
+ if (filelist[k * 256])
+ {
+ strcpy(filename + pathlen, &filelist[k * 256]);
+ k++;
+ }
+ else
+ break;
+ }
+
+ // Open ini.
+ if (f_open(&fp, filename, FA_READ) != FR_OK)
+ {
+ free(filelist);
+ free(filename);
+
+ return 0;
+ }
+
+ do
+ {
+ // Fetch one line.
+ lbuf[0] = 0;
+ f_gets(lbuf, 512, &fp);
+ lblen = strlen(lbuf);
+
+ // Remove trailing newline. Depends on 'FF_USE_STRFUNC 2' that removes \r.
+ if (lblen && lbuf[lblen - 1] == '\n')
+ lbuf[lblen - 1] = 0;
+
+ if (lblen > 2 && lbuf[0] == '[') // Create new section.
+ {
+ _find_section_name(lbuf, lblen, ']');
+
+ csec = _ini_create_section(dst, csec, &lbuf[1], INI_CHOICE);
+ list_init(&csec->kvs);
+ }
+ else if (lblen > 1 && lbuf[0] == '{') // Create new caption. Support empty caption '{}'.
+ {
+ _find_section_name(lbuf, lblen, '}');
+
+ csec = _ini_create_section(dst, csec, &lbuf[1], INI_CAPTION);
+ csec->color = 0xFF0AB9E6;
+ }
+ else if (lblen > 2 && lbuf[0] == '#') // Create comment.
+ {
+ csec = _ini_create_section(dst, csec, &lbuf[1], INI_COMMENT);
+ }
+ else if (lblen < 2) // Create empty line.
+ {
+ csec = _ini_create_section(dst, csec, NULL, INI_NEWLINE);
+ }
+ else if (csec && csec->type == INI_CHOICE) // Extract key/value.
+ {
+ u32 i = _find_section_name(lbuf, lblen, '=');
+
+ ini_kv_t *kv = (ini_kv_t *)calloc(sizeof(ini_kv_t), 1);
+ kv->key = _strdup(&lbuf[0]);
+ kv->val = _strdup(&lbuf[i + 1]);
+ list_append(&csec->kvs, &kv->link);
+ }
+ } while (!f_eof(&fp));
+
+ f_close(&fp);
+
+ if (csec)
+ {
+ list_append(dst, &csec->link);
+ if (is_dir)
+ csec = NULL;
+ }
+ } while (is_dir);
+
+ free(filename);
+ free(filelist);
+
+ return 1;
+}
+
+char *ini_check_payload_section(ini_sec_t *cfg)
+{
+ if (cfg == NULL)
+ return NULL;
+
+ LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg->kvs, link)
+ {
+ if (!strcmp("payload", kv->key))
+ return kv->val;
+ }
+
+ return NULL;
+}
diff --git a/bdk/utils/ini.h b/bdk/utils/ini.h
new file mode 100644
index 00000000..571d19d2
--- /dev/null
+++ b/bdk/utils/ini.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2018 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _INI_H_
+#define _INI_H_
+
+#include
+#include
+
+#define INI_CHOICE 3
+#define INI_CAPTION 5
+#define INI_CHGLINE 6
+#define INI_NEWLINE 0xFE
+#define INI_COMMENT 0xFF
+
+typedef struct _ini_kv_t
+{
+ char *key;
+ char *val;
+ link_t link;
+} ini_kv_t;
+
+typedef struct _ini_sec_t
+{
+ char *name;
+ link_t kvs;
+ link_t link;
+ u32 type;
+ u32 color;
+} ini_sec_t;
+
+int ini_parse(link_t *dst, char *ini_path, bool is_dir);
+char *ini_check_payload_section(ini_sec_t *cfg);
+
+#endif
+
diff --git a/bdk/utils/list.h b/bdk/utils/list.h
new file mode 100644
index 00000000..ece5402f
--- /dev/null
+++ b/bdk/utils/list.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 naehrwert
+ * Copyright (c) 2020 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _LIST_H_
+#define _LIST_H_
+
+#include
+
+/*! Initialize list. */
+#define LIST_INIT(name) link_t name = {&name, &name}
+
+/*! Initialize static list. */
+#define LIST_INIT_STATIC(name) static link_t name = {&name, &name}
+
+/*! Iterate over all list links. */
+#define LIST_FOREACH(iter, list) \
+ for(link_t *iter = (list)->next; iter != (list); iter = iter->next)
+
+/*! Safely iterate over all list links. */
+#define LIST_FOREACH_SAFE(iter, list) \
+ for(link_t *iter = (list)->next, *safe = iter->next; iter != (list); iter = safe, safe = iter->next)
+
+/*! Iterate over all list members and make sure that the list has at least one entry. */
+#define LIST_FOREACH_ENTRY(etype, iter, list, mn) \
+ if ((list)->next != (list)) \
+ for(etype *iter = CONTAINER_OF((list)->next, etype, mn); &iter->mn != (list); iter = CONTAINER_OF(iter->mn.next, etype, mn))
+
+typedef struct _link_t
+{
+ struct _link_t *prev;
+ struct _link_t *next;
+} link_t;
+
+static inline void link_init(link_t *l)
+{
+ l->prev = NULL;
+ l->next = NULL;
+}
+
+static inline int link_used(link_t *l)
+{
+ if(l->next == NULL)
+ return 1;
+ return 0;
+}
+
+static inline void list_init(link_t *lh)
+{
+ lh->prev = lh;
+ lh->next = lh;
+}
+
+static inline void list_prepend(link_t *lh, link_t *l)
+{
+ l->next = lh->next;
+ l->prev = lh;
+ lh->next->prev = l;
+ lh->next = l;
+}
+
+static inline void list_append(link_t *lh, link_t *l)
+{
+ l->prev = lh->prev;
+ l->next = lh;
+ lh->prev->next = l;
+ lh->prev = l;
+}
+
+static inline void list_remove(link_t *l)
+{
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ link_init(l);
+}
+
+static inline int list_empty(link_t *lh)
+{
+ if(lh->next == lh)
+ return 1;
+ return 0;
+}
+
+#endif
diff --git a/bdk/utils/sprintf.c b/bdk/utils/sprintf.c
new file mode 100644
index 00000000..aa5b9523
--- /dev/null
+++ b/bdk/utils/sprintf.c
@@ -0,0 +1,142 @@
+/*
+* Copyright (c) 2018 naehrwert
+* Copyright (c) 2019-2020 CTCaer
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms and conditions of the GNU General Public License,
+* version 2, as published by the Free Software Foundation.
+*
+* This program is distributed in the hope it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+#include
+#include
+
+#include
+
+char **sout_buf;
+
+static void _s_putc(char c)
+{
+ **sout_buf = c;
+ *sout_buf += 1;
+}
+
+static void _s_puts(char *s)
+{
+ for (; *s; s++)
+ _s_putc(*s);
+}
+
+static void _s_putn(u32 v, int base, char fill, int fcnt)
+{
+ char buf[65];
+ static const char digits[] = "0123456789ABCDEFghijklmnopqrstuvwxyz";
+ char *p;
+ int c = fcnt;
+
+ if (base > 36)
+ return;
+
+ p = buf + 64;
+ *p = 0;
+ do
+ {
+ c--;
+ *--p = digits[v % base];
+ v /= base;
+ } while (v);
+
+ if (fill != 0)
+ {
+ while (c > 0)
+ {
+ *--p = fill;
+ c--;
+ }
+ }
+
+ _s_puts(p);
+}
+
+void s_printf(char *out_buf, const char *fmt, ...)
+{
+ va_list ap;
+ int fill, fcnt;
+
+ sout_buf = &out_buf;
+
+ va_start(ap, fmt);
+ while(*fmt)
+ {
+ if(*fmt == '%')
+ {
+ fmt++;
+ fill = 0;
+ fcnt = 0;
+ if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ')
+ {
+ fcnt = *fmt;
+ fmt++;
+ if (*fmt >= '0' && *fmt <= '9')
+ {
+ fill = fcnt;
+ fcnt = *fmt - '0';
+ fmt++;
+ }
+ else
+ {
+ fill = ' ';
+ fcnt -= '0';
+ }
+ }
+ switch(*fmt)
+ {
+ case 'c':
+ _s_putc(va_arg(ap, u32));
+ break;
+ case 's':
+ _s_puts(va_arg(ap, char *));
+ break;
+ case 'd':
+ _s_putn(va_arg(ap, u32), 10, fill, fcnt);
+ break;
+ case 'p':
+ case 'P':
+ case 'x':
+ case 'X':
+ _s_putn(va_arg(ap, u32), 16, fill, fcnt);
+ break;
+ case 'k':
+ //gfx_con.fgcol = va_arg(ap, u32);
+ break;
+ case 'K':
+ //gfx_con.bgcol = va_arg(ap, u32);
+ //gfx_con.fillbg = 1;
+ break;
+ case '%':
+ _s_putc('%');
+ break;
+ case '\0':
+ goto out;
+ default:
+ _s_putc('%');
+ _s_putc(*fmt);
+ break;
+ }
+ }
+ else
+ _s_putc(*fmt);
+ fmt++;
+ }
+
+out:
+ **sout_buf = '\0';
+ va_end(ap);
+}
\ No newline at end of file
diff --git a/bdk/utils/sprintf.h b/bdk/utils/sprintf.h
new file mode 100644
index 00000000..845f9b74
--- /dev/null
+++ b/bdk/utils/sprintf.h
@@ -0,0 +1,24 @@
+/*
+* Copyright (c) 2019 CTCaer
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms and conditions of the GNU General Public License,
+* version 2, as published by the Free Software Foundation.
+*
+* This program is distributed in the hope it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+#ifndef _SPRINTF_H_
+#define _SPRINTF_H_
+
+#include
+
+void s_printf(char *out_buf, const char *fmt, ...);
+
+#endif
\ No newline at end of file
diff --git a/bdk/utils/types.h b/bdk/utils/types.h
new file mode 100644
index 00000000..96615abb
--- /dev/null
+++ b/bdk/utils/types.h
@@ -0,0 +1,128 @@
+/*
+* Copyright (c) 2018 naehrwert
+* Copyright (c) 2018-2020 CTCaer
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms and conditions of the GNU General Public License,
+* version 2, as published by the Free Software Foundation.
+*
+* This program is distributed in the hope it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+#define NULL ((void *)0)
+
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define ALIGN_DOWN(x, a) (((x) - ((a) - 1)) & ~((a) - 1))
+#define BIT(n) (1U << (n))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+#define OFFSET_OF(t, m) ((u32)&((t *)NULL)->m)
+#define CONTAINER_OF(mp, t, mn) ((t *)((u32)mp - OFFSET_OF(t, mn)))
+
+typedef signed char s8;
+typedef short s16;
+typedef short SHORT;
+typedef int s32;
+typedef int INT;
+typedef long LONG;
+typedef long long int s64;
+typedef unsigned char u8;
+typedef unsigned char BYTE;
+typedef unsigned short u16;
+typedef unsigned short WORD;
+typedef unsigned short WCHAR;
+typedef unsigned int u32;
+typedef unsigned int UINT;
+typedef unsigned long DWORD;
+typedef unsigned long long QWORD;
+typedef unsigned long long int u64;
+typedef volatile unsigned char vu8;
+typedef volatile unsigned short vu16;
+typedef volatile unsigned int vu32;
+
+#ifdef __aarch64__
+typedef u64 uptr;
+#else /* __arm__ or __thumb__ */
+typedef u32 uptr;
+#endif
+
+typedef int bool;
+#define true 1
+#define false 0
+
+#define DISABLE 0
+#define ENABLE 1
+
+#define BOOT_CFG_AUTOBOOT_EN BIT(0)
+#define BOOT_CFG_FROM_LAUNCH BIT(1)
+#define BOOT_CFG_FROM_ID BIT(2)
+#define BOOT_CFG_TO_EMUMMC BIT(3)
+#define BOOT_CFG_SEPT_RUN BIT(7)
+
+#define EXTRA_CFG_KEYS BIT(0)
+#define EXTRA_CFG_PAYLOAD BIT(1)
+#define EXTRA_CFG_MODULE BIT(2)
+
+#define EXTRA_CFG_NYX_BIS BIT(4)
+#define EXTRA_CFG_NYX_UMS BIT(5)
+#define EXTRA_CFG_NYX_RELOAD BIT(6)
+#define EXTRA_CFG_NYX_DUMP BIT(7)
+
+typedef enum _nyx_ums_type
+{
+ NYX_UMS_SD_CARD = 0,
+ NYX_UMS_EMMC_BOOT0,
+ NYX_UMS_EMMC_BOOT1,
+ NYX_UMS_EMMC_GPP,
+ NYX_UMS_EMUMMC_BOOT0,
+ NYX_UMS_EMUMMC_BOOT1,
+ NYX_UMS_EMUMMC_GPP
+} nyx_ums_type;
+
+typedef struct __attribute__((__packed__)) _boot_cfg_t
+{
+ u8 boot_cfg;
+ u8 autoboot;
+ u8 autoboot_list;
+ u8 extra_cfg;
+ union
+ {
+ struct
+ {
+ char id[8]; // 7 char ASCII null teminated.
+ char emummc_path[0x78]; // emuMMC/XXX, ASCII null teminated.
+ };
+ u8 ums; // nyx_ums_type.
+ u8 xt_str[0x80];
+ };
+} boot_cfg_t;
+
+typedef struct __attribute__((__packed__)) _ipl_ver_meta_t
+{
+ u32 magic;
+ u32 version;
+ u16 rsvd0;
+ u16 rsvd1;
+} ipl_ver_meta_t;
+
+typedef struct __attribute__((__packed__)) _reloc_meta_t
+{
+ u32 start;
+ u32 stack;
+ u32 end;
+ u32 ep;
+} reloc_meta_t;
+
+#endif
diff --git a/bdk/utils/util.c b/bdk/utils/util.c
new file mode 100644
index 00000000..bb5ae2dd
--- /dev/null
+++ b/bdk/utils/util.c
@@ -0,0 +1,242 @@
+/*
+* Copyright (c) 2018 naehrwert
+* Copyright (c) 2018-2020 CTCaer
+* Copyright (c) 2020 Storm
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms and conditions of the GNU General Public License,
+* version 2, as published by the Free Software Foundation.
+*
+* This program is distributed in the hope it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+#include