aboutsummaryrefslogtreecommitdiffstats
diff options
-rw-r--r--pci-msi-fix.patch130
-rw-r--r--series5
-rw-r--r--staging-exfat-add-filesystem-to-drivers-staging-exfat.patch11166
3 files changed, 11301 insertions, 0 deletions
diff --git a/pci-msi-fix.patch b/pci-msi-fix.patch
new file mode 100644
index 00000000000000..5f000ccc930b5c
--- /dev/null
+++ b/pci-msi-fix.patch
@@ -0,0 +1,130 @@
+---
+ drivers/pci/msi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/pci.h | 1
+ 2 files changed, 86 insertions(+)
+
+--- a/drivers/pci/msi.c
++++ b/drivers/pci/msi.c
+@@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, str
+ static void free_msi_irqs(struct pci_dev *dev)
+ {
+ struct msi_desc *entry, *tmp;
++ struct attribute **msi_attrs;
++ struct device_attribute *dev_attr;
++ int count = 0;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int i, nvec;
+@@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev
+ list_del(&entry->list);
+ kfree(entry);
+ }
++
++ if (dev->msi_irq_groups) {
++ sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups);
++ msi_attrs = dev->msi_irq_groups[0]->attrs;
++ list_for_each_entry(entry, &dev->msi_list, list) {
++ dev_attr = container_of(msi_attrs[count],
++ struct device_attribute, attr);
++ kfree(dev_attr->attr.name);
++ kfree(dev_attr);
++ ++count;
++ }
++ kfree(msi_attrs);
++ kfree(dev->msi_irq_groups[0]);
++ kfree(dev->msi_irq_groups);
++ dev->msi_irq_groups = NULL;
++ }
+ }
+
+ static struct msi_desc *alloc_msi_entry(struct pci_dev *dev)
+@@ -527,13 +546,79 @@ static struct kobj_type msi_irq_ktype =
+ .default_attrs = msi_irq_default_attrs,
+ };
+
++static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct msi_desc *entry;
++ unsigned long irq;
++ int retval;
++
++ retval = kstrtoul(attr->attr.name, 10, &irq);
++ if (retval)
++ return retval;
++
++ list_for_each_entry(entry, &pdev->msi_list, list) {
++ if (entry->irq == irq) {
++ return sprintf(buf, "%s\n",
++ entry->msi_attrib.is_msix ? "msix" : "msi");
++ }
++ }
++ return -ENODEV;
++}
++
+ static int populate_msi_sysfs(struct pci_dev *pdev)
+ {
++ struct attribute **msi_attrs;
++ struct device_attribute *msi_dev_attr;
++ struct attribute_group *msi_irq_group;
++ const struct attribute_group **msi_irq_groups;
+ struct msi_desc *entry;
+ struct kobject *kobj;
+ int ret;
++ int num_msi = 0;
+ int count = 0;
+
++ /* Determine how many msi entries we have */
++ list_for_each_entry(entry, &pdev->msi_list, list) {
++ ++num_msi;
++ }
++ if (!num_msi)
++ return 0;
++
++ /* Dynamically create the MSI attributes for the PCI device */
++ msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL);
++ if (!msi_attrs)
++ return -ENOMEM;
++ list_for_each_entry(entry, &pdev->msi_list, list) {
++ char *name = kmalloc(20, GFP_KERNEL);
++ msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
++ if (!msi_dev_attr)
++ return -ENOMEM;
++ sprintf(name, "%d", entry->irq);
++ msi_dev_attr->attr.name = name;
++ msi_dev_attr->attr.mode = S_IRUGO;
++ msi_dev_attr->show = msi_mode_show;
++ msi_attrs[count] = &msi_dev_attr->attr;
++ ++count;
++ }
++
++ msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL);
++ if (!msi_irq_group)
++ return -ENOMEM;
++ msi_irq_group->name = "msi_irqs_2";
++ msi_irq_group->attrs = msi_attrs;
++
++ msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL);
++ if (!msi_irq_groups)
++ return -ENOMEM;
++ msi_irq_groups[0] = msi_irq_group;
++
++ ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups);
++ if (ret)
++ return ret;
++ pdev->msi_irq_groups = msi_irq_groups;
++
+ pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj);
+ if (!pdev->msi_kset)
+ return -ENOMEM;
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -352,6 +352,7 @@ struct pci_dev {
+ #ifdef CONFIG_PCI_MSI
+ struct list_head msi_list;
+ struct kset *msi_kset;
++ const struct attribute_group **msi_irq_groups;
+ #endif
+ struct pci_vpd *vpd;
+ #ifdef CONFIG_PCI_ATS
diff --git a/series b/series
index 18f660d7731a44..ff5b8ffeae0760 100644
--- a/series
+++ b/series
@@ -1,3 +1,8 @@
+staging-exfat-add-filesystem-to-drivers-staging-exfat.patch
+
+
+
+pci-msi-fix.patch
xen-disable-clock-timer-when-shutting-down.patch
# patches already in my git trees, but still here so I don't loose them.
diff --git a/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch b/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch
new file mode 100644
index 00000000000000..137f8ae908ee4d
--- /dev/null
+++ b/staging-exfat-add-filesystem-to-drivers-staging-exfat.patch
@@ -0,0 +1,11166 @@
+Date: Fri, 16 Aug 2013 12:52:00 -0800
+Subject: staging: exfat: add filesystem to drivers/staging/exfat
+
+From: Joe Kim <someone@samsung.com>
+
+This is the exfat code version 1.2.4 as found on the
+http://opensource.samsung.com site in the zip file exfat_1.2.4.zip with
+the contents:
+$ sha256sum exfat_1.2.4.zip
+4cb39c4f44b7a68cc391596384744a32f948ba659697542ff771764b215810f3 exfat_1.2.4.zip
+
+Released under the GNU General Public License, version 2, as specified
+from that web site.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/exfat/Makefile | 13 +
+ drivers/staging/exfat/README | 21 +
+ drivers/staging/exfat/exfat.c | 4950 +++++++++++++++++++++++++++++++++
+ drivers/staging/exfat/exfat.h | 598 ++++
+ drivers/staging/exfat/exfat_api.c | 457 +++
+ drivers/staging/exfat/exfat_api.h | 165 ++
+ drivers/staging/exfat/exfat_blkdev.c | 156 ++
+ drivers/staging/exfat/exfat_blkdev.h | 46 +
+ drivers/staging/exfat/exfat_cache.c | 740 +++++
+ drivers/staging/exfat/exfat_cache.h | 63 +
+ drivers/staging/exfat/exfat_config.h | 57 +
+ drivers/staging/exfat/exfat_data.c | 40 +
+ drivers/staging/exfat/exfat_data.h | 39 +
+ drivers/staging/exfat/exfat_global.c | 119 +
+ drivers/staging/exfat/exfat_global.h | 127 +
+ drivers/staging/exfat/exfat_nls.c | 347 +++
+ drivers/staging/exfat/exfat_nls.h | 68 +
+ drivers/staging/exfat/exfat_oal.c | 146 +
+ drivers/staging/exfat/exfat_oal.h | 53 +
+ drivers/staging/exfat/exfat_part.h | 54 +
+ drivers/staging/exfat/exfat_super.c | 2154 ++++++++++++++
+ drivers/staging/exfat/exfat_super.h | 149 +
+ drivers/staging/exfat/exfat_upcase.c | 390 +++
+ drivers/staging/exfat/exfat_version.h | 1 +
+ 24 files changed, 10953 insertions(+)
+ create mode 100644 drivers/staging/exfat/Makefile
+ create mode 100644 drivers/staging/exfat/README
+ create mode 100644 drivers/staging/exfat/exfat.c
+ create mode 100644 drivers/staging/exfat/exfat.h
+ create mode 100644 drivers/staging/exfat/exfat_api.c
+ create mode 100644 drivers/staging/exfat/exfat_api.h
+ create mode 100644 drivers/staging/exfat/exfat_blkdev.c
+ create mode 100644 drivers/staging/exfat/exfat_blkdev.h
+ create mode 100644 drivers/staging/exfat/exfat_cache.c
+ create mode 100644 drivers/staging/exfat/exfat_cache.h
+ create mode 100644 drivers/staging/exfat/exfat_config.h
+ create mode 100644 drivers/staging/exfat/exfat_data.c
+ create mode 100644 drivers/staging/exfat/exfat_data.h
+ create mode 100644 drivers/staging/exfat/exfat_global.c
+ create mode 100644 drivers/staging/exfat/exfat_global.h
+ create mode 100644 drivers/staging/exfat/exfat_nls.c
+ create mode 100644 drivers/staging/exfat/exfat_nls.h
+ create mode 100644 drivers/staging/exfat/exfat_oal.c
+ create mode 100644 drivers/staging/exfat/exfat_oal.h
+ create mode 100644 drivers/staging/exfat/exfat_part.h
+ create mode 100644 drivers/staging/exfat/exfat_super.c
+ create mode 100644 drivers/staging/exfat/exfat_super.h
+ create mode 100644 drivers/staging/exfat/exfat_upcase.c
+ create mode 100644 drivers/staging/exfat/exfat_version.h
+
+diff --git a/drivers/staging/exfat/Makefile b/drivers/staging/exfat/Makefile
+new file mode 100644
+index 00000000..5ff84c8d
+--- /dev/null
++++ b/drivers/staging/exfat/Makefile
+@@ -0,0 +1,13 @@
++obj-m += exfat_core.o exfat_fs.o
++
++exfat_fs-y := exfat_super.o
++
++exfat_core-y := exfat.o exfat_api.o exfat_blkdev.o exfat_cache.o \
++ exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o
++
++all:
++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
++
++clean:
++ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
++
+diff --git a/drivers/staging/exfat/README b/drivers/staging/exfat/README
+new file mode 100644
+index 00000000..32a1f5f3
+--- /dev/null
++++ b/drivers/staging/exfat/README
+@@ -0,0 +1,21 @@
++HOW TO BUILD YOUR KERNEL WITH THE exFAT FILESYSTEM DRIVER
++
++- get Kernel code of your Samsung Product in OSRC website. (http://opensource.samsung.com/)
++
++- make "exfat" directory into {Kernel}/drivers/
++
++- copy exFAT source code into {Kernel}/drivers/exfat/
++
++- edit default kernel configuration file of your device.
++$ vi {Kernel}/arch/arm/configs/{your_device_defconfig}
++#NLS Setting
++CONFIG_NLS_UTF8=y
++
++- edit a Makefile in Kernel/drivers
++$ vi {Kernel}/drivers/Makefile
++obj-y += exfat/
++
++- execute command to make for your Kernel. (see README_kernel.txt in Kernel)
++ ex) make arch=arm defconfig
++ make
++
+diff --git a/drivers/staging/exfat/exfat.c b/drivers/staging/exfat/exfat.c
+new file mode 100644
+index 00000000..c7af0e7d
+--- /dev/null
++++ b/drivers/staging/exfat/exfat.c
+@@ -0,0 +1,4950 @@
++/* Some of the source code in this file came from "linux/fs/fat/misc.c". */
++/*
++ * linux/fs/fat/misc.c
++ *
++ * Written 1992,1993 by Werner Almesberger
++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
++ */
++
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include <linux/version.h>
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_blkdev.h"
++#include "exfat_cache.h"
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat_super.h"
++#include "exfat.h"
++
++#include <linux/blkdev.h>
++
++#define THERE_IS_MBR 0
++
++#if (THERE_IS_MBR == 1)
++#include "exfat_part.h"
++#endif
++
++#define DELAYED_SYNC 0
++
++#define ELAPSED_TIME 0
++
++#if (ELAPSED_TIME == 1)
++#include <linux/time.h>
++
++static UINT32 __t1, __t2;
++static UINT32 get_current_msec(void)
++{
++ struct timeval tm;
++ do_gettimeofday(&tm);
++ return (UINT32)(tm.tv_sec*1000000 + tm.tv_usec);
++}
++#define TIME_START() do {__t1 = get_current_msec(); } while (0)
++#define TIME_END() do {__t2 = get_current_msec(); } while (0)
++#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)); } while (0)
++#else
++#define TIME_START()
++#define TIME_END()
++#define PRINT_TIME(n)
++#endif
++
++static void __set_sb_dirty(struct super_block *sb)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
++ sb->s_dirt = 1;
++#else
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ sbi->s_dirt = 1;
++#endif
++}
++
++extern UINT8 uni_upcase[];
++
++static UINT8 name_buf[MAX_PATH_LENGTH *MAX_CHARSET_SIZE];
++
++static INT8 *reserved_names[] = {
++ "AUX ", "CON ", "NUL ", "PRN ",
++ "COM1 ", "COM2 ", "COM3 ", "COM4 ",
++ "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ",
++ "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ",
++ "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ",
++ NULL
++};
++
++static UINT8 free_bit[] = {
++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,
++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,
++ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,
++ 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,
++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,
++ 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,
++ 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,
++ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,
++ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,
++ 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
++ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
++};
++
++static UINT8 used_bit[] = {
++ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,
++ 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,
++ 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
++ 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
++ 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
++ 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,
++ 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,
++ 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
++ 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,
++ 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
++ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
++};
++
++INT32 ffsInit(void)
++{
++ INT32 ret;
++
++ ret = bdev_init();
++ if (ret)
++ return ret;
++
++ ret = fs_init();
++ if (ret)
++ return ret;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsShutdown(void)
++{
++ INT32 ret;
++ ret = fs_shutdown();
++ if (ret)
++ return ret;
++
++ ret = bdev_shutdown();
++ if (ret)
++ return ret;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsMountVol(struct super_block *sb, INT32 drv)
++{
++ INT32 i, ret;
++#if (THERE_IS_MBR == 1)
++ MBR_SECTOR_T *p_mbr;
++ PART_ENTRY_T *p_pte;
++#endif
++ PBR_SECTOR_T *p_pbr;
++ struct buffer_head *tmp_bh = NULL;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ printk("[EXFAT] trying to mount...\n");
++
++ p_fs->drv = drv;
++ p_fs->dev_ejected = FALSE;
++
++ if (bdev_open(sb))
++ return FFS_MEDIAERR;
++
++ if (p_bd->sector_size < sb->s_blocksize)
++ return FFS_MEDIAERR;
++ if (p_bd->sector_size > sb->s_blocksize)
++ sb_set_blocksize(sb, p_bd->sector_size);
++
++ if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS)
++ return FFS_MEDIAERR;
++
++#if (THERE_IS_MBR == 1)
++ if (buf[0] != 0xEB) {
++ p_mbr = (MBR_SECTOR_T *) tmp_bh->b_data;
++
++ if (GET16_A(p_mbr->signature) != MBR_SIGNATURE) {
++ brelse(tmp_bh);
++ bdev_close(sb);
++ return FFS_FORMATERR;
++ }
++
++ p_pte = (PART_ENTRY_T *) p_mbr->partition + 0;
++ p_fs->PBR_sector = GET32(p_pte->start_sector);
++ p_fs->num_sectors = GET32(p_pte->num_sectors);
++
++ if (p_fs->num_sectors == 0) {
++ brelse(tmp_bh);
++ bdev_close(sb);
++ return FFS_ERROR;
++ }
++
++ if (sector_read(sb, p_fs->PBR_sector, &tmp_bh, 1) != FFS_SUCCESS) {
++ bdev_close(sb);
++ return FFS_MEDIAERR;
++ }
++ } else {
++#endif
++ p_fs->PBR_sector = 0;
++#if (THERE_IS_MBR == 1)
++ }
++#endif
++
++ p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data;
++
++ if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) {
++ brelse(tmp_bh);
++ bdev_close(sb);
++ return FFS_FORMATERR;
++ }
++
++ for (i = 0; i < 53; i++)
++ if (p_pbr->bpb[i])
++ break;
++
++ if (i < 53) {
++ if (GET16(p_pbr->bpb+11))
++ ret = fat16_mount(sb, p_pbr);
++ else
++ ret = fat32_mount(sb, p_pbr);
++ } else {
++ ret = exfat_mount(sb, p_pbr);
++ }
++
++ brelse(tmp_bh);
++
++ if (ret) {
++ bdev_close(sb);
++ return ret;
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ ret = load_alloc_bitmap(sb);
++ if (ret) {
++ bdev_close(sb);
++ return ret;
++ }
++ ret = load_upcase_table(sb);
++ if (ret) {
++ free_alloc_bitmap(sb);
++ bdev_close(sb);
++ return ret;
++ }
++ }
++
++ if (p_fs->dev_ejected) {
++ if (p_fs->vol_type == EXFAT) {
++ free_upcase_table(sb);
++ free_alloc_bitmap(sb);
++ }
++ bdev_close(sb);
++ return FFS_MEDIAERR;
++ }
++
++ printk("[EXFAT] mounted successfully\n");
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsUmountVol(struct super_block *sb)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ printk("[EXFAT] trying to unmount...\n");
++
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++
++ if (p_fs->vol_type == EXFAT) {
++ free_upcase_table(sb);
++ free_alloc_bitmap(sb);
++ }
++
++ FAT_release_all(sb);
++ buf_release_all(sb);
++
++ bdev_close(sb);
++
++ if (p_fs->dev_ejected) {
++ printk( "[EXFAT] unmounted with media errors. "
++ "device's already ejected.\n");
++ return FFS_MEDIAERR;
++ }
++
++ printk("[EXFAT] unmounted successfully\n");
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_fs->used_clusters == (UINT32) ~0)
++ p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb);
++
++ info->FatType = p_fs->vol_type;
++ info->ClusterSize = p_fs->cluster_size;
++ info->NumClusters = p_fs->num_clusters - 2;
++ info->UsedClusters = p_fs->used_clusters;
++ info->FreeClusters = info->NumClusters - info->UsedClusters;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ fs_sync(sb, do_sync);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid)
++{
++ INT32 ret, dentry, num_entries;
++ CHAIN_T dir;
++ UNI_NAME_T uni_name;
++ DOS_NAME_T dos_name;
++ DENTRY_T *ep, *ep2;
++ ENTRY_SET_CACHE_T *es=NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ PRINTK("ffsLookupFile entered\n");
++
++ ret = resolve_path(inode, path, &dir, &uni_name);
++ if (ret)
++ return ret;
++
++ ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name);
++ if (ret)
++ return ret;
++
++ dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL);
++ if (dentry < -1)
++ return FFS_NOTFOUND;
++
++ fid->dir.dir = dir.dir;
++ fid->dir.size = dir.size;
++ fid->dir.flags = dir.flags;
++ fid->entry = dentry;
++
++ if (dentry == -1) {
++ fid->type = TYPE_DIR;
++ fid->rwoffset = 0;
++ fid->hint_last_off = -1;
++
++ fid->attr = ATTR_SUBDIR;
++ fid->flags = 0x01;
++ fid->size = 0;
++ fid->start_clu = p_fs->root_dir;
++ } else {
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep);
++ if (!es)
++ return FFS_MEDIAERR;
++ ep2 = ep+1;
++ } else {
++ ep = get_entry_in_dir(sb, &dir, dentry, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++ ep2 = ep;
++ }
++
++ fid->type = p_fs->fs_func->get_entry_type(ep);
++ fid->rwoffset = 0;
++ fid->hint_last_off = -1;
++ fid->attr = p_fs->fs_func->get_entry_attr(ep);
++
++ fid->size = p_fs->fs_func->get_entry_size(ep2);
++ if ((fid->type == TYPE_FILE) && (fid->size == 0)) {
++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
++ fid->start_clu = CLUSTER_32(~0);
++ } else {
++ fid->flags = p_fs->fs_func->get_entry_flag(ep2);
++ fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2);
++ }
++
++ if (p_fs->vol_type == EXFAT)
++ release_entry_set(es);
++ }
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ PRINTK("ffsLookupFile exited successfully\n");
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid)
++{
++ INT32 ret;
++ CHAIN_T dir;
++ UNI_NAME_T uni_name;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ ret = resolve_path(inode, path, &dir, &uni_name);
++ if (ret)
++ return ret;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++ ret = create_file(inode, &dir, &uni_name, mode, fid);
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return ret;
++}
++
++INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount)
++{
++ INT32 offset, sec_offset, clu_offset;
++ UINT32 clu, LogSector;
++ UINT64 oneblkread, read_bytes;
++ struct buffer_head *tmp_bh = NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (fid->type != TYPE_FILE)
++ return FFS_PERMISSIONERR;
++
++ if (fid->rwoffset > fid->size)
++ fid->rwoffset = fid->size;
++
++ if (count > (fid->size - fid->rwoffset))
++ count = fid->size - fid->rwoffset;
++
++ if (count == 0) {
++ if (rcount != NULL)
++ *rcount = 0;
++ return FFS_EOF;
++ }
++
++ read_bytes = 0;
++
++ while (count > 0) {
++ clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits);
++ clu = fid->start_clu;
++
++ if (fid->flags == 0x03) {
++ clu += clu_offset;
++ } else {
++ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
++ (clu_offset >= fid->hint_last_off)) {
++ clu_offset -= fid->hint_last_off;
++ clu = fid->hint_last_clu;
++ }
++
++ while (clu_offset > 0) {
++ if (FAT_read(sb, clu, &clu) == -1)
++ return FFS_MEDIAERR;
++
++ clu_offset--;
++ }
++ }
++
++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits);
++ fid->hint_last_clu = clu;
++
++ offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1));
++ sec_offset = offset >> p_bd->sector_size_bits;
++ offset &= p_bd->sector_size_mask;
++
++ LogSector = START_SECTOR(clu) + sec_offset;
++
++ oneblkread = (UINT64)(p_bd->sector_size - offset);
++ if (oneblkread > count)
++ oneblkread = count;
++
++ if ((offset == 0) && (oneblkread == p_bd->sector_size)) {
++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS)
++ goto err_out;
++ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data), (INT32) oneblkread);
++ } else {
++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS)
++ goto err_out;
++ MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data)+offset, (INT32) oneblkread);
++ }
++ count -= oneblkread;
++ read_bytes += oneblkread;
++ fid->rwoffset += oneblkread;
++ }
++ brelse(tmp_bh);
++
++err_out:
++ if (rcount != NULL)
++ *rcount = read_bytes;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount)
++{
++ INT32 modified = FALSE, offset, sec_offset, clu_offset;
++ INT32 num_clusters, num_alloc, num_alloced = (INT32) ~0;
++ UINT32 clu, last_clu, LogSector, sector;
++ UINT64 oneblkwrite, write_bytes;
++ CHAIN_T new_clu;
++ TIMESTAMP_T tm;
++ DENTRY_T *ep, *ep2;
++ ENTRY_SET_CACHE_T *es = NULL;
++ struct buffer_head *tmp_bh = NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (fid->type != TYPE_FILE)
++ return FFS_PERMISSIONERR;
++
++ if (fid->rwoffset > fid->size)
++ fid->rwoffset = fid->size;
++
++ if (count == 0) {
++ if (wcount != NULL)
++ *wcount = 0;
++ return FFS_SUCCESS;
++ }
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ if (fid->size == 0)
++ num_clusters = 0;
++ else
++ num_clusters = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
++
++ write_bytes = 0;
++
++ while (count > 0) {
++ clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits);
++ clu = last_clu = fid->start_clu;
++
++ if (fid->flags == 0x03) {
++ if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) {
++ last_clu += clu_offset - 1;
++
++ if (clu_offset == num_clusters)
++ clu = CLUSTER_32(~0);
++ else
++ clu += clu_offset;
++ }
++ } else {
++ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
++ (clu_offset >= fid->hint_last_off)) {
++ clu_offset -= fid->hint_last_off;
++ clu = fid->hint_last_clu;
++ }
++
++ while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) {
++ last_clu = clu;
++ if (FAT_read(sb, clu, &clu) == -1)
++ return FFS_MEDIAERR;
++
++ clu_offset--;
++ }
++ }
++
++ if (clu == CLUSTER_32(~0)) {
++ num_alloc = (INT32)((count-1) >> p_fs->cluster_size_bits) + 1;
++ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1;
++ new_clu.size = 0;
++ new_clu.flags = fid->flags;
++
++ num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu);
++ if (num_alloced == 0)
++ break;
++
++ if (last_clu == CLUSTER_32(~0)) {
++ if (new_clu.flags == 0x01)
++ fid->flags = 0x01;
++ fid->start_clu = new_clu.dir;
++ modified = TRUE;
++ } else {
++ if (new_clu.flags != fid->flags) {
++ exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters);
++ fid->flags = 0x01;
++ modified = TRUE;
++ }
++ if (new_clu.flags == 0x01)
++ FAT_write(sb, last_clu, new_clu.dir);
++ }
++
++ num_clusters += num_alloced;
++ clu = new_clu.dir;
++ }
++
++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits);
++ fid->hint_last_clu = clu;
++
++ offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1));
++ sec_offset = offset >> p_bd->sector_size_bits;
++ offset &= p_bd->sector_size_mask;
++
++ LogSector = START_SECTOR(clu) + sec_offset;
++
++ oneblkwrite = (UINT64)(p_bd->sector_size - offset);
++ if (oneblkwrite > count)
++ oneblkwrite = count;
++
++ if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) {
++ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS)
++ goto err_out;
++ MEMCPY(((INT8 *) tmp_bh->b_data), ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite);
++ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) {
++ brelse(tmp_bh);
++ goto err_out;
++ }
++ } else {
++ if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) {
++ if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS)
++ goto err_out;
++ } else {
++ if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS)
++ goto err_out;
++ }
++
++ MEMCPY(((INT8 *) tmp_bh->b_data)+offset, ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite);
++ if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) {
++ brelse(tmp_bh);
++ goto err_out;
++ }
++ }
++
++ count -= oneblkwrite;
++ write_bytes += oneblkwrite;
++ fid->rwoffset += oneblkwrite;
++
++ fid->attr |= ATTR_ARCHIVE;
++
++ if (fid->size < fid->rwoffset) {
++ fid->size = fid->rwoffset;
++ modified = TRUE;
++ }
++ }
++
++ brelse(tmp_bh);
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL)
++ goto err_out;
++ ep2 = ep+1;
++ } else {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ goto err_out;
++ ep2 = ep;
++ }
++
++ p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY);
++ p_fs->fs_func->set_entry_attr(ep, fid->attr);
++
++ if (p_fs->vol_type != EXFAT)
++ buf_modify(sb, sector);
++
++ if (modified) {
++ if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags)
++ p_fs->fs_func->set_entry_flag(ep2, fid->flags);
++
++ if (p_fs->fs_func->get_entry_size(ep2) != fid->size)
++ p_fs->fs_func->set_entry_size(ep2, fid->size);
++
++ if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu)
++ p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu);
++
++ if (p_fs->vol_type != EXFAT)
++ buf_modify(sb, sector);
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ update_dir_checksum_with_entry_set(sb, es);
++ release_entry_set(es);
++ }
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++err_out:
++ if (wcount != NULL)
++ *wcount = write_bytes;
++
++ if (num_alloced == 0)
++ return FFS_FULL;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size)
++{
++ INT32 num_clusters;
++ UINT32 last_clu = CLUSTER_32(0), sector;
++ CHAIN_T clu;
++ TIMESTAMP_T tm;
++ DENTRY_T *ep, *ep2;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++ ENTRY_SET_CACHE_T *es=NULL;
++
++ if (fid->type != TYPE_FILE)
++ return FFS_PERMISSIONERR;
++
++ if (fid->size != old_size) {
++ printk(KERN_ERR "[EXFAT] truncate : can't skip it because of "
++ "size-mismatch(old:%lld->fid:%lld).\n"
++ ,old_size, fid->size);
++ }
++
++ if (old_size <= new_size)
++ return FFS_SUCCESS;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ clu.dir = fid->start_clu;
++ clu.size = (INT32)((old_size-1) >> p_fs->cluster_size_bits) + 1;
++ clu.flags = fid->flags;
++
++ if (new_size > 0) {
++ num_clusters = (INT32)((new_size-1) >> p_fs->cluster_size_bits) + 1;
++
++ if (clu.flags == 0x03) {
++ clu.dir += num_clusters;
++ } else {
++ while (num_clusters > 0) {
++ last_clu = clu.dir;
++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1)
++ return FFS_MEDIAERR;
++ num_clusters--;
++ }
++ }
++
++ clu.size -= num_clusters;
++ }
++
++ fid->size = new_size;
++ fid->attr |= ATTR_ARCHIVE;
++ if (new_size == 0) {
++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
++ fid->start_clu = CLUSTER_32(~0);
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL)
++ return FFS_MEDIAERR;
++ ep2 = ep+1;
++ } else {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++ ep2 = ep;
++ }
++
++ p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY);
++ p_fs->fs_func->set_entry_attr(ep, fid->attr);
++
++ p_fs->fs_func->set_entry_size(ep2, new_size);
++ if (new_size == 0) {
++ p_fs->fs_func->set_entry_flag(ep2, 0x01);
++ p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0));
++ }
++
++ if (p_fs->vol_type != EXFAT)
++ buf_modify(sb, sector);
++ else {
++ update_dir_checksum_with_entry_set(sb, es);
++ release_entry_set(es);
++ }
++
++ if (last_clu != CLUSTER_32(0)) {
++ if (fid->flags == 0x01)
++ FAT_write(sb, last_clu, CLUSTER_32(~0));
++ }
++
++ p_fs->fs_func->free_cluster(sb, &clu, 0);
++
++ fid->hint_last_off = -1;
++ if (fid->rwoffset > fid->size) {
++ fid->rwoffset = fid->size;
++ }
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++static void update_parent_info( FILE_ID_T *fid, struct inode *parent_inode)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info);
++ FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid);
++
++ if (unlikely((parent_fid->flags != fid->dir.flags)
++ || (parent_fid->size != (fid->dir.size<<p_fs->cluster_size_bits))
++ || (parent_fid->start_clu != fid->dir.dir))) {
++
++ fid->dir.dir = parent_fid->start_clu;
++ fid->dir.flags = parent_fid->flags;
++ fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1))
++ >> p_fs->cluster_size_bits);
++ }
++}
++
++INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry)
++{
++ INT32 ret;
++ INT32 dentry;
++ CHAIN_T olddir, newdir;
++ CHAIN_T *p_dir=NULL;
++ UNI_NAME_T uni_name;
++ DENTRY_T *ep;
++ struct super_block *sb = old_parent_inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ UINT8 *new_path = (UINT8 *) new_dentry->d_name.name;
++ struct inode *new_inode = new_dentry->d_inode;
++ int num_entries;
++ FILE_ID_T *new_fid = NULL;
++ INT32 new_entry=0;
++
++ if ((new_path == NULL) || (STRLEN(new_path) == 0))
++ return FFS_ERROR;
++
++ update_parent_info(fid, old_parent_inode);
++
++ olddir.dir = fid->dir.dir;
++ olddir.size = fid->dir.size;
++ olddir.flags = fid->dir.flags;
++
++ dentry = fid->entry;
++
++ if (p_fs->vol_type != EXFAT) {
++ if ((olddir.dir != p_fs->root_dir) && (dentry < 2))
++ return FFS_PERMISSIONERR;
++ }
++
++ ep = get_entry_in_dir(sb, &olddir, dentry, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY)
++ return FFS_PERMISSIONERR;
++
++ if (new_inode) {
++ UINT32 entry_type;
++
++ ret = FFS_MEDIAERR;
++ new_fid = &EXFAT_I(new_inode)->fid;
++
++ update_parent_info(new_fid, new_parent_inode);
++
++ p_dir = &(new_fid->dir);
++ new_entry = new_fid->entry;
++ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL);
++ if (!ep)
++ goto out;
++
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if (entry_type == TYPE_DIR) {
++ CHAIN_T new_clu;
++ new_clu.dir = new_fid->start_clu;
++ new_clu.size = (INT32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1;
++ new_clu.flags = new_fid->flags;
++
++ if (!is_dir_empty(sb, &new_clu))
++ return FFS_FILEEXIST;
++ }
++ }
++
++ ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name);
++ if (ret)
++ return ret;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ if (olddir.dir == newdir.dir)
++ ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid);
++ else
++ ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid);
++
++ if ((ret == FFS_SUCCESS) && new_inode) {
++ ep = get_entry_in_dir(sb, p_dir, new_entry, NULL);
++ if (!ep)
++ goto out;
++
++ num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep);
++ if (num_entries < 0)
++ goto out;
++ p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1);
++ }
++out:
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return ret;
++}
++
++INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid)
++{
++ INT32 dentry;
++ CHAIN_T dir, clu_to_free;
++ DENTRY_T *ep;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ dir.dir = fid->dir.dir;
++ dir.size = fid->dir.size;
++ dir.flags = fid->dir.flags;
++
++ dentry = fid->entry;
++
++ ep = get_entry_in_dir(sb, &dir, dentry, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY)
++ return FFS_PERMISSIONERR;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ remove_file(inode, &dir, dentry);
++
++ clu_to_free.dir = fid->start_clu;
++ clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
++ clu_to_free.flags = fid->flags;
++
++ p_fs->fs_func->free_cluster(sb, &clu_to_free, 0);
++
++
++ fid->size = 0;
++ fid->start_clu = CLUSTER_32(~0);
++ fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01;
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsSetAttr(struct inode *inode, UINT32 attr)
++{
++ UINT32 type, sector;
++ DENTRY_T *ep;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
++ ENTRY_SET_CACHE_T *es = NULL;
++
++ if (fid->attr == attr) {
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++ return FFS_SUCCESS;
++ }
++
++ if (is_dir) {
++ if ((fid->dir.dir == p_fs->root_dir) &&
++ (fid->entry == -1)) {
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++ return FFS_SUCCESS;
++ }
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL)
++ return FFS_MEDIAERR;
++ } else {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++ }
++
++ type = p_fs->fs_func->get_entry_type(ep);
++
++ if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) ||
++ ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) {
++ INT32 err;
++ if (p_fs->dev_ejected)
++ err = FFS_MEDIAERR;
++ else
++ err = FFS_ERROR;
++
++ if (p_fs->vol_type == EXFAT)
++ release_entry_set(es);
++ return err;
++ }
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ fid->attr = attr;
++ p_fs->fs_func->set_entry_attr(ep, attr);
++
++ if (p_fs->vol_type != EXFAT)
++ buf_modify(sb, sector);
++ else {
++ update_dir_checksum_with_entry_set(sb, es);
++ release_entry_set(es);
++ }
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info)
++{
++ UINT32 sector;
++ INT32 count;
++ CHAIN_T dir;
++ UNI_NAME_T uni_name;
++ TIMESTAMP_T tm;
++ DENTRY_T *ep, *ep2;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++ ENTRY_SET_CACHE_T *es=NULL;
++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
++
++ PRINTK("ffsGetStat entered\n");
++
++ if (is_dir) {
++ if ((fid->dir.dir == p_fs->root_dir) &&
++ (fid->entry == -1)) {
++ info->Attr = ATTR_SUBDIR;
++ MEMSET((INT8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T));
++ MEMSET((INT8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T));
++ MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T));
++ STRCPY(info->ShortName, ".");
++ STRCPY(info->Name, ".");
++
++ dir.dir = p_fs->root_dir;
++ dir.flags = 0x01;
++
++ if (p_fs->root_dir == CLUSTER_32(0))
++ info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS;
++ else
++ info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits;
++
++ count = count_dos_name_entries(sb, &dir, TYPE_DIR);
++ if (count < 0)
++ return FFS_MEDIAERR;
++ info->NumSubdirs = count;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++ return FFS_SUCCESS;
++ }
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep);
++ if (es == NULL)
++ return FFS_MEDIAERR;
++ ep2 = ep+1;
++ } else {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++ ep2 = ep;
++ buf_lock(sb, sector);
++ }
++
++ info->Attr = p_fs->fs_func->get_entry_attr(ep);
++
++ p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE);
++ info->CreateTimestamp.Year = tm.year;
++ info->CreateTimestamp.Month = tm.mon;
++ info->CreateTimestamp.Day = tm.day;
++ info->CreateTimestamp.Hour = tm.hour;
++ info->CreateTimestamp.Minute = tm.min;
++ info->CreateTimestamp.Second = tm.sec;
++ info->CreateTimestamp.MilliSecond = 0;
++
++ p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
++ info->ModifyTimestamp.Year = tm.year;
++ info->ModifyTimestamp.Month = tm.mon;
++ info->ModifyTimestamp.Day = tm.day;
++ info->ModifyTimestamp.Hour = tm.hour;
++ info->ModifyTimestamp.Minute = tm.min;
++ info->ModifyTimestamp.Second = tm.sec;
++ info->ModifyTimestamp.MilliSecond = 0;
++
++ MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T));
++
++ *(uni_name.name) = 0x0;
++ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name);
++ if (*(uni_name.name) == 0x0)
++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1);
++ nls_uniname_to_cstring(sb, info->Name, &uni_name);
++
++ if (p_fs->vol_type == EXFAT) {
++ info->NumSubdirs = 2;
++ } else {
++ buf_unlock(sb, sector);
++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0);
++ nls_uniname_to_cstring(sb, info->ShortName, &uni_name);
++ info->NumSubdirs = 0;
++ }
++
++ info->Size = p_fs->fs_func->get_entry_size(ep2);
++
++ if (p_fs->vol_type == EXFAT)
++ release_entry_set(es);
++
++ if (is_dir) {
++ dir.dir = fid->start_clu;
++ dir.flags = 0x01;
++
++ if (info->Size == 0)
++ info->Size = (UINT64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits;
++
++ count = count_dos_name_entries(sb, &dir, TYPE_DIR);
++ if (count < 0)
++ return FFS_MEDIAERR;
++ info->NumSubdirs += count;
++ }
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ PRINTK("ffsGetStat exited successfully\n");
++ return FFS_SUCCESS;
++}
++
++INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info)
++{
++ UINT32 sector;
++ TIMESTAMP_T tm;
++ DENTRY_T *ep, *ep2;
++ ENTRY_SET_CACHE_T *es=NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++ UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0;
++
++ if (is_dir) {
++ if ((fid->dir.dir == p_fs->root_dir) &&
++ (fid->entry == -1)) {
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++ return FFS_SUCCESS;
++ }
++ }
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL)
++ return FFS_MEDIAERR;
++ ep2 = ep+1;
++ } else {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++ ep2 = ep;
++ }
++
++
++ p_fs->fs_func->set_entry_attr(ep, info->Attr);
++
++ tm.sec = info->CreateTimestamp.Second;
++ tm.min = info->CreateTimestamp.Minute;
++ tm.hour = info->CreateTimestamp.Hour;
++ tm.day = info->CreateTimestamp.Day;
++ tm.mon = info->CreateTimestamp.Month;
++ tm.year = info->CreateTimestamp.Year;
++ p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE);
++
++ tm.sec = info->ModifyTimestamp.Second;
++ tm.min = info->ModifyTimestamp.Minute;
++ tm.hour = info->ModifyTimestamp.Hour;
++ tm.day = info->ModifyTimestamp.Day;
++ tm.mon = info->ModifyTimestamp.Month;
++ tm.year = info->ModifyTimestamp.Year;
++ p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY);
++
++
++ p_fs->fs_func->set_entry_size(ep2, info->Size);
++
++ if (p_fs->vol_type != EXFAT) {
++ buf_modify(sb, sector);
++ } else {
++ update_dir_checksum_with_entry_set(sb, es);
++ release_entry_set(es);
++ }
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu)
++{
++ INT32 num_clusters, num_alloced, modified = FALSE;
++ UINT32 last_clu, sector;
++ CHAIN_T new_clu;
++ DENTRY_T *ep;
++ ENTRY_SET_CACHE_T *es = NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++
++ fid->rwoffset = (INT64)(clu_offset) << p_fs->cluster_size_bits;
++
++ if (EXFAT_I(inode)->mmu_private == 0)
++ num_clusters = 0;
++ else
++ num_clusters = (INT32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1;
++
++ *clu = last_clu = fid->start_clu;
++
++ if (fid->flags == 0x03) {
++ if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) {
++ last_clu += clu_offset - 1;
++
++ if (clu_offset == num_clusters)
++ *clu = CLUSTER_32(~0);
++ else
++ *clu += clu_offset;
++ }
++ } else {
++ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
++ (clu_offset >= fid->hint_last_off)) {
++ clu_offset -= fid->hint_last_off;
++ *clu = fid->hint_last_clu;
++ }
++
++ while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) {
++ last_clu = *clu;
++ if (FAT_read(sb, *clu, clu) == -1)
++ return FFS_MEDIAERR;
++ clu_offset--;
++ }
++ }
++
++ if (*clu == CLUSTER_32(~0)) {
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1;
++ new_clu.size = 0;
++ new_clu.flags = fid->flags;
++
++ num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu);
++ if (num_alloced < 1)
++ return FFS_FULL;
++
++ if (last_clu == CLUSTER_32(~0)) {
++ if (new_clu.flags == 0x01)
++ fid->flags = 0x01;
++ fid->start_clu = new_clu.dir;
++ modified = TRUE;
++ } else {
++ if (new_clu.flags != fid->flags) {
++ exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters);
++ fid->flags = 0x01;
++ modified = TRUE;
++ }
++ if (new_clu.flags == 0x01)
++ FAT_write(sb, last_clu, new_clu.dir);
++ }
++
++ num_clusters += num_alloced;
++ *clu = new_clu.dir;
++
++ if (p_fs->vol_type == EXFAT) {
++ es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL)
++ return FFS_MEDIAERR;
++ ep++;
++ }
++
++ if (modified) {
++ if (p_fs->vol_type != EXFAT) {
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++ }
++
++ if (p_fs->fs_func->get_entry_flag(ep) != fid->flags)
++ p_fs->fs_func->set_entry_flag(ep, fid->flags);
++
++ if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu)
++ p_fs->fs_func->set_entry_clu0(ep, fid->start_clu);
++
++ if (p_fs->vol_type != EXFAT)
++ buf_modify(sb, sector);
++ }
++
++ if (p_fs->vol_type == EXFAT) {
++ update_dir_checksum_with_entry_set(sb, es);
++ release_entry_set(es);
++ }
++
++ inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9);
++ }
++
++ fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits);
++ fid->hint_last_clu = *clu;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid)
++{
++ INT32 ret;
++ CHAIN_T dir;
++ UNI_NAME_T uni_name;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ PRINTK("ffsCreateDir entered\n");
++
++ ret = resolve_path(inode, path, &dir, &uni_name);
++ if (ret)
++ return ret;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ ret = create_dir(inode, &dir, &uni_name, fid);
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return ret;
++}
++
++INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry)
++{
++ INT32 i, dentry, clu_offset;
++ INT32 dentries_per_clu, dentries_per_clu_bits = 0;
++ UINT32 type, sector;
++ CHAIN_T dir, clu;
++ UNI_NAME_T uni_name;
++ TIMESTAMP_T tm;
++ DENTRY_T *ep;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++
++ if (fid->type != TYPE_DIR)
++ return FFS_PERMISSIONERR;
++
++ if (fid->entry == -1) {
++ dir.dir = p_fs->root_dir;
++ dir.flags = 0x01;
++ } else {
++ dir.dir = fid->start_clu;
++ dir.size = (INT32)(fid->size >> p_fs->cluster_size_bits);
++ dir.flags = fid->flags;
++ }
++
++ dentry = (INT32) fid->rwoffset;
++
++ if (dir.dir == CLUSTER_32(0)) {
++ dentries_per_clu = p_fs->dentries_in_root;
++
++ if (dentry == dentries_per_clu) {
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ clu.dir = dir.dir;
++ clu.size = dir.size;
++ clu.flags = dir.flags;
++ }
++ } else {
++ dentries_per_clu = p_fs->dentries_per_clu;
++ dentries_per_clu_bits = my_log2(dentries_per_clu);
++
++ clu_offset = dentry >> dentries_per_clu_bits;
++ clu.dir = dir.dir;
++ clu.size = dir.size;
++ clu.flags = dir.flags;
++
++ if (clu.flags == 0x03) {
++ clu.dir += clu_offset;
++ clu.size -= clu_offset;
++ } else {
++ if ((clu_offset > 0) && (fid->hint_last_off > 0) &&
++ (clu_offset >= fid->hint_last_off)) {
++ clu_offset -= fid->hint_last_off;
++ clu.dir = fid->hint_last_clu;
++ }
++
++ while (clu_offset > 0) {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1)
++ return FFS_MEDIAERR;
++
++ clu_offset--;
++ }
++ }
++ }
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ if (dir.dir == CLUSTER_32(0))
++ i = dentry % dentries_per_clu;
++ else
++ i = dentry & (dentries_per_clu-1);
++
++ for ( ; i < dentries_per_clu; i++, dentry++) {
++ ep = get_entry_in_dir(sb, &clu, i, &sector);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ type = p_fs->fs_func->get_entry_type(ep);
++
++ if (type == TYPE_UNUSED)
++ break;
++
++ if ((type != TYPE_FILE) && (type != TYPE_DIR))
++ continue;
++
++ buf_lock(sb, sector);
++ dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep);
++
++ p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE);
++ dir_entry->CreateTimestamp.Year = tm.year;
++ dir_entry->CreateTimestamp.Month = tm.mon;
++ dir_entry->CreateTimestamp.Day = tm.day;
++ dir_entry->CreateTimestamp.Hour = tm.hour;
++ dir_entry->CreateTimestamp.Minute = tm.min;
++ dir_entry->CreateTimestamp.Second = tm.sec;
++ dir_entry->CreateTimestamp.MilliSecond = 0;
++
++ p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY);
++ dir_entry->ModifyTimestamp.Year = tm.year;
++ dir_entry->ModifyTimestamp.Month = tm.mon;
++ dir_entry->ModifyTimestamp.Day = tm.day;
++ dir_entry->ModifyTimestamp.Hour = tm.hour;
++ dir_entry->ModifyTimestamp.Minute = tm.min;
++ dir_entry->ModifyTimestamp.Second = tm.sec;
++ dir_entry->ModifyTimestamp.MilliSecond = 0;
++
++ MEMSET((INT8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T));
++
++ *(uni_name.name) = 0x0;
++ p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name);
++ if (*(uni_name.name) == 0x0)
++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1);
++ nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name);
++ buf_unlock(sb, sector);
++
++ if (p_fs->vol_type == EXFAT) {
++ ep = get_entry_in_dir(sb, &clu, i+1, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++ } else {
++ get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0);
++ nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name);
++ }
++
++ dir_entry->Size = p_fs->fs_func->get_entry_size(ep);
++
++ if (dir.dir == CLUSTER_32(0)) {
++ } else {
++ fid->hint_last_off = dentry >> dentries_per_clu_bits;
++ fid->hint_last_clu = clu.dir;
++ }
++
++ fid->rwoffset = (INT64) ++dentry;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++ }
++
++ if (dir.dir == CLUSTER_32(0))
++ break;
++
++ if (clu.flags == 0x03) {
++ if ((--clu.size) > 0)
++ clu.dir++;
++ else
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) == -1)
++ return FFS_MEDIAERR;
++ }
++ }
++
++ *(dir_entry->Name) = '\0';
++
++ fid->rwoffset = (INT64) ++dentry;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid)
++{
++ INT32 dentry;
++ CHAIN_T dir, clu_to_free;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ dir.dir = fid->dir.dir;
++ dir.size = fid->dir.size;
++ dir.flags = fid->dir.flags;
++
++ dentry = fid->entry;
++
++ if (p_fs->vol_type != EXFAT) {
++ if ((dir.dir != p_fs->root_dir) && (dentry < 2))
++ return FFS_PERMISSIONERR;
++ }
++
++ clu_to_free.dir = fid->start_clu;
++ clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1;
++ clu_to_free.flags = fid->flags;
++
++ if (!is_dir_empty(sb, &clu_to_free))
++ return FFS_FILEEXIST;
++
++ fs_set_vol_flags(sb, VOL_DIRTY);
++
++ remove_file(inode, &dir, dentry);
++
++ p_fs->fs_func->free_cluster(sb, &clu_to_free, 1);
++
++ fid->size = 0;
++ fid->start_clu = CLUSTER_32(~0);
++ fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01;
++
++#if (DELAYED_SYNC == 0)
++ fs_sync(sb, 0);
++ fs_set_vol_flags(sb, VOL_CLEAN);
++#endif
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ return FFS_SUCCESS;
++}
++
++INT32 fs_init(void)
++{
++ if (sizeof(DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) {
++ return FFS_ALIGNMENTERR;
++ }
++
++ return FFS_SUCCESS;
++}
++
++INT32 fs_shutdown(void)
++{
++ return FFS_SUCCESS;
++}
++
++void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag)
++{
++ PBR_SECTOR_T *p_pbr;
++ BPBEX_T *p_bpb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_fs->vol_flag == new_flag)
++ return;
++
++ p_fs->vol_flag = new_flag;
++
++ if (p_fs->vol_type == EXFAT) {
++ if (p_fs->pbr_bh == NULL) {
++ if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS)
++ return;
++ }
++
++ p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data;
++ p_bpb = (BPBEX_T *) p_pbr->bpb;
++ SET16(p_bpb->vol_flags, (UINT16) new_flag);
++
++ if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh)))
++ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1);
++ else
++ sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0);
++ }
++}
++
++void fs_sync(struct super_block *sb, INT32 do_sync)
++{
++ if (do_sync)
++ bdev_sync(sb);
++}
++
++void fs_error(struct super_block *sb)
++{
++ struct exfat_mount_options *opts = &EXFAT_SB(sb)->options;
++
++ if (opts->errors == EXFAT_ERRORS_PANIC)
++ panic("[EXFAT] Filesystem panic from previous error\n");
++ else if ((opts->errors == EXFAT_ERRORS_RO) && !(sb->s_flags & MS_RDONLY)) {
++ sb->s_flags |= MS_RDONLY;
++ printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n");
++ }
++}
++
++INT32 clear_cluster(struct super_block *sb, UINT32 clu)
++{
++ UINT32 s, n;
++ INT32 ret = FFS_SUCCESS;
++ struct buffer_head *tmp_bh = NULL;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (clu == CLUSTER_32(0)) {
++ s = p_fs->root_start_sector;
++ n = p_fs->data_start_sector;
++ } else {
++ s = START_SECTOR(clu);
++ n = s + p_fs->sectors_per_clu;
++ }
++
++ for ( ; s < n; s++) {
++ if ((ret = sector_read(sb, s, &tmp_bh, 0)) != FFS_SUCCESS)
++ return ret;
++
++ MEMSET((INT8 *) tmp_bh->b_data, 0x0, p_bd->sector_size);
++ if ((ret = sector_write(sb, s, tmp_bh, 0)) !=FFS_SUCCESS)
++ break;
++ }
++
++ brelse(tmp_bh);
++ return ret;
++}
++
++INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain)
++{
++ INT32 i, num_clusters = 0;
++ UINT32 new_clu, last_clu = CLUSTER_32(~0), read_clu;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ new_clu = p_chain->dir;
++ if (new_clu == CLUSTER_32(~0))
++ new_clu = p_fs->clu_srch_ptr;
++ else if (new_clu >= p_fs->num_clusters)
++ new_clu = 2;
++
++ __set_sb_dirty(sb);
++
++ p_chain->dir = CLUSTER_32(~0);
++
++ for (i = 2; i < p_fs->num_clusters; i++) {
++ if (FAT_read(sb, new_clu, &read_clu) != 0)
++ return 0;
++
++ if (read_clu == CLUSTER_32(0)) {
++ FAT_write(sb, new_clu, CLUSTER_32(~0));
++ num_clusters++;
++
++ if (p_chain->dir == CLUSTER_32(~0))
++ p_chain->dir = new_clu;
++ else
++ FAT_write(sb, last_clu, new_clu);
++
++ last_clu = new_clu;
++
++ if ((--num_alloc) == 0) {
++ p_fs->clu_srch_ptr = new_clu;
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters += num_clusters;
++
++ return(num_clusters);
++ }
++ }
++ if ((++new_clu) >= p_fs->num_clusters)
++ new_clu = 2;
++ }
++
++ p_fs->clu_srch_ptr = new_clu;
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters += num_clusters;
++
++ return(num_clusters);
++}
++
++INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain)
++{
++ INT32 num_clusters = 0;
++ UINT32 hint_clu, new_clu, last_clu = CLUSTER_32(~0);
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ hint_clu = p_chain->dir;
++ if (hint_clu == CLUSTER_32(~0)) {
++ hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2);
++ if (hint_clu == CLUSTER_32(~0))
++ return 0;
++ } else if (hint_clu >= p_fs->num_clusters) {
++ hint_clu = 2;
++ p_chain->flags = 0x01;
++ }
++
++ __set_sb_dirty(sb);
++
++ p_chain->dir = CLUSTER_32(~0);
++
++ while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) {
++ if (new_clu != hint_clu) {
++ if (p_chain->flags == 0x03) {
++ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters);
++ p_chain->flags = 0x01;
++ }
++ }
++
++ if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS)
++ return 0;
++
++ num_clusters++;
++
++ if (p_chain->flags == 0x01)
++ FAT_write(sb, new_clu, CLUSTER_32(~0));
++
++ if (p_chain->dir == CLUSTER_32(~0)) {
++ p_chain->dir = new_clu;
++ } else {
++ if (p_chain->flags == 0x01)
++ FAT_write(sb, last_clu, new_clu);
++ }
++ last_clu = new_clu;
++
++ if ((--num_alloc) == 0) {
++ p_fs->clu_srch_ptr = hint_clu;
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters += num_clusters;
++
++ p_chain->size += num_clusters;
++ return(num_clusters);
++ }
++
++ hint_clu = new_clu + 1;
++ if (hint_clu >= p_fs->num_clusters) {
++ hint_clu = 2;
++
++ if (p_chain->flags == 0x03) {
++ exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters);
++ p_chain->flags = 0x01;
++ }
++ }
++ }
++
++ p_fs->clu_srch_ptr = hint_clu;
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters += num_clusters;
++
++ p_chain->size += num_clusters;
++ return(num_clusters);
++}
++
++void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse)
++{
++ INT32 num_clusters = 0;
++ UINT32 clu, prev;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ INT32 i;
++ UINT32 sector;
++
++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
++ return;
++ __set_sb_dirty(sb);
++ clu = p_chain->dir;
++
++ if (p_chain->size <= 0)
++ return;
++
++ do {
++ if (p_fs->dev_ejected)
++ break;
++
++ if (do_relse) {
++ sector = START_SECTOR(clu);
++ for (i = 0; i < p_fs->sectors_per_clu; i++) {
++ buf_release(sb, sector+i);
++ }
++ }
++
++ prev = clu;
++ if (FAT_read(sb, clu, &clu) == -1)
++ break;
++
++ FAT_write(sb, prev, CLUSTER_32(0));
++ num_clusters++;
++
++ } while (clu != CLUSTER_32(~0));
++
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters -= num_clusters;
++}
++
++void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse)
++{
++ INT32 num_clusters = 0;
++ UINT32 clu;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ INT32 i;
++ UINT32 sector;
++
++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
++ return;
++
++ if (p_chain->size <= 0) {
++ printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, "
++ "because of zero-size truncation\n"
++ ,p_chain->dir);
++ return;
++ }
++
++ __set_sb_dirty(sb);
++ clu = p_chain->dir;
++
++ if (p_chain->flags == 0x03) {
++ do {
++ if (do_relse) {
++ sector = START_SECTOR(clu);
++ for (i = 0; i < p_fs->sectors_per_clu; i++) {
++ buf_release(sb, sector+i);
++ }
++ }
++
++ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS)
++ break;
++ clu++;
++
++ num_clusters++;
++ } while (num_clusters < p_chain->size);
++ } else {
++ do {
++ if (p_fs->dev_ejected)
++ break;
++
++ if (do_relse) {
++ sector = START_SECTOR(clu);
++ for (i = 0; i < p_fs->sectors_per_clu; i++) {
++ buf_release(sb, sector+i);
++ }
++ }
++
++ if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS)
++ break;
++
++ if (FAT_read(sb, clu, &clu) == -1)
++ break;
++ num_clusters++;
++ } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0)));
++ }
++
++ if (p_fs->used_clusters != (UINT32) ~0)
++ p_fs->used_clusters -= num_clusters;
++}
++
++UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain)
++{
++ UINT32 clu, next;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ clu = p_chain->dir;
++
++ if (p_chain->flags == 0x03) {
++ clu += p_chain->size - 1;
++ } else {
++ while((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) {
++ if (p_fs->dev_ejected)
++ break;
++ clu = next;
++ }
++ }
++
++ return(clu);
++}
++
++INT32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain)
++{
++ INT32 i, count = 0;
++ UINT32 clu;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
++ return 0;
++
++ clu = p_chain->dir;
++
++ if (p_chain->flags == 0x03) {
++ count = p_chain->size;
++ } else {
++ for (i = 2; i < p_fs->num_clusters; i++) {
++ count++;
++ if (FAT_read(sb, clu, &clu) != 0)
++ return 0;
++ if (clu == CLUSTER_32(~0))
++ break;
++ }
++ }
++
++ return(count);
++}
++
++INT32 fat_count_used_clusters(struct super_block *sb)
++{
++ INT32 i, count = 0;
++ UINT32 clu;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ for (i = 2; i < p_fs->num_clusters; i++) {
++ if (FAT_read(sb, i, &clu) != 0)
++ break;
++ if (clu != CLUSTER_32(0))
++ count++;
++ }
++
++ return(count);
++}
++
++INT32 exfat_count_used_clusters(struct super_block *sb)
++{
++ INT32 i, map_i, map_b, count = 0;
++ UINT8 k;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ map_i = map_b = 0;
++
++ for (i = 2; i < p_fs->num_clusters; i += 8) {
++ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
++ count += used_bit[k];
++
++ if ((++map_b) >= p_bd->sector_size) {
++ map_i++;
++ map_b = 0;
++ }
++ }
++
++ return(count);
++}
++
++void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len)
++{
++ if (len == 0)
++ return;
++
++ while (len > 1) {
++ FAT_write(sb, chain, chain+1);
++ chain++;
++ len--;
++ }
++ FAT_write(sb, chain, CLUSTER_32(~0));
++}
++
++INT32 load_alloc_bitmap(struct super_block *sb)
++{
++ INT32 i, j, ret;
++ UINT32 map_size;
++ UINT32 type, sector;
++ CHAIN_T clu;
++ BMAP_DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ clu.dir = p_fs->root_dir;
++ clu.flags = 0x01;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < p_fs->dentries_per_clu; i++) {
++ ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep);
++
++ if (type == TYPE_UNUSED)
++ break;
++ if (type != TYPE_BITMAP)
++ continue;
++
++ if (ep->flags == 0x0) {
++ p_fs->map_clu = GET32_A(ep->start_clu);
++ map_size = (UINT32) GET64_A(ep->size);
++
++ p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1;
++
++ p_fs->vol_amap = (struct buffer_head **) MALLOC(sizeof(struct buffer_head *) * p_fs->map_sectors);
++ if (p_fs->vol_amap == NULL)
++ return FFS_MEMORYERR;
++
++ sector = START_SECTOR(p_fs->map_clu);
++
++ for (j = 0; j < p_fs->map_sectors; j++) {
++ p_fs->vol_amap[j] = NULL;
++ ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1);
++ if (ret != FFS_SUCCESS) {
++ i=0;
++ while (i < j)
++ brelse(p_fs->vol_amap[i++]);
++
++ FREE(p_fs->vol_amap);
++ p_fs->vol_amap = NULL;
++ return ret;
++ }
++ }
++
++ p_fs->pbr_bh = NULL;
++ return FFS_SUCCESS;
++ }
++ }
++
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return FFS_MEDIAERR;
++ }
++
++ return FFS_FORMATERR;
++}
++
++void free_alloc_bitmap(struct super_block *sb)
++{
++ INT32 i;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ brelse(p_fs->pbr_bh);
++
++ for (i = 0; i < p_fs->map_sectors; i++) {
++ __brelse(p_fs->vol_amap[i]);
++ }
++
++ FREE(p_fs->vol_amap);
++ p_fs->vol_amap = NULL;
++}
++
++INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu)
++{
++ INT32 i, b;
++ UINT32 sector;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ i = clu >> (p_bd->sector_size_bits + 3);
++ b = clu & ((p_bd->sector_size << 3) - 1);
++
++ sector = START_SECTOR(p_fs->map_clu) + i;
++
++ Bitmap_set((UINT8 *) p_fs->vol_amap[i]->b_data, b);
++
++ return (sector_write(sb, sector, p_fs->vol_amap[i], 0));
++}
++
++INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu)
++{
++ INT32 i, b;
++ UINT32 sector;
++#if EXFAT_CONFIG_DISCARD
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ struct exfat_mount_options *opts = &sbi->options;
++ int ret;
++#endif
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ i = clu >> (p_bd->sector_size_bits + 3);
++ b = clu & ((p_bd->sector_size << 3) - 1);
++
++ sector = START_SECTOR(p_fs->map_clu) + i;
++
++ Bitmap_clear((UINT8 *) p_fs->vol_amap[i]->b_data, b);
++
++ return (sector_write(sb, sector, p_fs->vol_amap[i], 0));
++
++#if EXFAT_CONFIG_DISCARD
++ if (opts->discard) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
++ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits));
++#else
++ ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0);
++#endif
++ if (ret == -EOPNOTSUPP) {
++ printk(KERN_WARNING "discard not supported by device, disabling");
++ opts->discard = 0;
++ }
++ }
++#endif
++}
++
++UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu)
++{
++ INT32 i, map_i, map_b;
++ UINT32 clu_base, clu_free;
++ UINT8 k, clu_mask;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ clu_base = (clu & ~(0x7)) + 2;
++ clu_mask = (1 << (clu - clu_base + 2)) - 1;
++
++ map_i = clu >> (p_bd->sector_size_bits + 3);
++ map_b = (clu >> 3) & p_bd->sector_size_mask;
++
++ for (i = 2; i < p_fs->num_clusters; i += 8) {
++ k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b);
++ if (clu_mask > 0) {
++ k |= clu_mask;
++ clu_mask = 0;
++ }
++ if (k < 0xFF) {
++ clu_free = clu_base + free_bit[k];
++ if (clu_free < p_fs->num_clusters)
++ return(clu_free);
++ }
++ clu_base += 8;
++
++ if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) {
++ if ((++map_i) >= p_fs->map_sectors) {
++ clu_base = 2;
++ map_i = 0;
++ }
++ map_b = 0;
++ }
++ }
++
++ return(CLUSTER_32(~0));
++}
++
++void sync_alloc_bitmap(struct super_block *sb)
++{
++ INT32 i;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_fs->vol_amap == NULL)
++ return;
++
++ for (i = 0; i < p_fs->map_sectors; i++) {
++ sync_dirty_buffer(p_fs->vol_amap[i]);
++ }
++}
++
++INT32 __load_upcase_table(struct super_block *sb, UINT32 sector, UINT32 num_sectors, UINT32 utbl_checksum)
++{
++ INT32 i, ret = FFS_ERROR;
++ UINT32 j;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ struct buffer_head *tmp_bh = NULL;
++
++ UINT8 skip = FALSE;
++ UINT32 index = 0;
++ UINT16 uni = 0;
++ UINT16 **upcase_table;
++
++ UINT32 checksum = 0;
++
++ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *));
++ if(upcase_table == NULL)
++ return FFS_MEMORYERR;
++ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *));
++
++ num_sectors += sector;
++
++ while(sector < num_sectors) {
++ ret = sector_read(sb, sector, &tmp_bh, 1);
++ if (ret != FFS_SUCCESS) {
++ PRINTK("sector read (0x%X)fail\n", sector);
++ goto error;
++ }
++ sector++;
++
++ for(i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) {
++ uni = GET16(((UINT8 *) tmp_bh->b_data)+i);
++
++ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+i);
++ checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+(i+1));
++
++ if(skip) {
++ PRINTK("skip from 0x%X ", index);
++ index += uni;
++ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni);
++ skip = FALSE;
++ } else if(uni == index)
++ index++;
++ else if(uni == 0xFFFF)
++ skip = TRUE;
++ else {
++ UINT16 col_index = get_col_index(index);
++
++ if(upcase_table[col_index]== NULL) {
++ PRINTK("alloc = 0x%X\n", col_index);
++ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16));
++ if(upcase_table[col_index] == NULL) {
++ ret = FFS_MEMORYERR;
++ goto error;
++ }
++
++ for(j = 0 ; j < UTBL_ROW_COUNT ; j++)
++ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
++ }
++
++ upcase_table[col_index][get_row_index(index)] = uni;
++ index++;
++ }
++ }
++ }
++ if(index >= 0xFFFF && utbl_checksum == checksum) {
++ if(tmp_bh)
++ brelse(tmp_bh);
++ return FFS_SUCCESS;
++ }
++ ret = FFS_ERROR;
++error:
++ if(tmp_bh)
++ brelse(tmp_bh);
++ free_upcase_table(sb);
++ return ret;
++}
++
++INT32 __load_default_upcase_table(struct super_block *sb)
++{
++ INT32 i, ret = FFS_ERROR;
++ UINT32 j;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ UINT8 skip = FALSE;
++ UINT32 index = 0;
++ UINT16 uni = 0;
++ UINT16 **upcase_table;
++
++ upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *));
++ if(upcase_table == NULL)
++ return FFS_MEMORYERR;
++ MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *));
++
++ for(i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) {
++ uni = GET16(uni_upcase + i);
++ if(skip) {
++ PRINTK("skip from 0x%X ", index);
++ index += uni;
++ PRINTK("to 0x%X (amount of 0x%X)\n", index, uni);
++ skip = FALSE;
++ } else if(uni == index)
++ index++;
++ else if(uni == 0xFFFF)
++ skip = TRUE;
++ else {
++ UINT16 col_index = get_col_index(index);
++
++ if(upcase_table[col_index]== NULL) {
++ PRINTK("alloc = 0x%X\n", col_index);
++ upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16));
++ if(upcase_table[col_index] == NULL) {
++ ret = FFS_MEMORYERR;
++ goto error;
++ }
++
++ for(j = 0 ; j < UTBL_ROW_COUNT ; j++)
++ upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
++ }
++
++ upcase_table[col_index][get_row_index(index)] = uni;
++ index ++;
++ }
++ }
++
++ if(index >= 0xFFFF)
++ return FFS_SUCCESS;
++
++error:
++ free_upcase_table(sb);
++ return ret;
++}
++
++INT32 load_upcase_table(struct super_block *sb)
++{
++ INT32 i;
++ UINT32 tbl_clu, tbl_size;
++ UINT32 type, sector, num_sectors;
++ CHAIN_T clu;
++ CASE_DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ clu.dir = p_fs->root_dir;
++ clu.flags = 0x01;
++
++ if (p_fs->dev_ejected)
++ return FFS_MEDIAERR;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ for (i = 0; i < p_fs->dentries_per_clu; i++) {
++ ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep);
++
++ if (type == TYPE_UNUSED)
++ break;
++ if (type != TYPE_UPCASE)
++ continue;
++
++ tbl_clu = GET32_A(ep->start_clu);
++ tbl_size = (UINT32) GET64_A(ep->size);
++
++ sector = START_SECTOR(tbl_clu);
++ num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1;
++ if(__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS)
++ break;
++ else
++ return FFS_SUCCESS;
++ }
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return FFS_MEDIAERR;
++ }
++ return __load_default_upcase_table(sb);
++}
++
++void free_upcase_table(struct super_block *sb)
++{
++ UINT32 i;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ UINT16 **upcase_table;
++
++ upcase_table = p_fs->vol_utbl;
++ for(i = 0 ; i < UTBL_COL_COUNT ; i ++)
++ FREE(upcase_table[i]);
++
++ FREE(p_fs->vol_utbl);
++
++ p_fs->vol_utbl = NULL;
++}
++
++UINT32 fat_get_entry_type(DENTRY_T *p_entry)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++
++ if (*(ep->name) == 0x0)
++ return TYPE_UNUSED;
++
++ else if (*(ep->name) == 0xE5)
++ return TYPE_DELETED;
++
++ else if (ep->attr == ATTR_EXTEND)
++ return TYPE_EXTEND;
++
++ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME)
++ return TYPE_VOLUME;
++
++ else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR)
++ return TYPE_DIR;
++
++ return TYPE_FILE;
++}
++
++UINT32 exfat_get_entry_type(DENTRY_T *p_entry)
++{
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++
++ if (ep->type == 0x0) {
++ return TYPE_UNUSED;
++ } else if (ep->type < 0x80) {
++ return TYPE_DELETED;
++ } else if (ep->type == 0x80) {
++ return TYPE_INVALID;
++ } else if (ep->type < 0xA0) {
++ if (ep->type == 0x81) {
++ return TYPE_BITMAP;
++ } else if (ep->type == 0x82) {
++ return TYPE_UPCASE;
++ } else if (ep->type == 0x83) {
++ return TYPE_VOLUME;
++ } else if (ep->type == 0x85) {
++ if (GET16_A(ep->attr) & ATTR_SUBDIR)
++ return TYPE_DIR;
++ else
++ return TYPE_FILE;
++ }
++ return TYPE_CRITICAL_PRI;
++ } else if (ep->type < 0xC0) {
++ if (ep->type == 0xA0) {
++ return TYPE_GUID;
++ } else if (ep->type == 0xA1) {
++ return TYPE_PADDING;
++ } else if (ep->type == 0xA2) {
++ return TYPE_ACLTAB;
++ }
++ return TYPE_BENIGN_PRI;
++ } else if (ep->type < 0xE0) {
++ if (ep->type == 0xC0) {
++ return TYPE_STREAM;
++ } else if (ep->type == 0xC1) {
++ return TYPE_EXTEND;
++ } else if (ep->type == 0xC2) {
++ return TYPE_ACL;
++ }
++ return TYPE_CRITICAL_SEC;
++ }
++
++ return TYPE_BENIGN_SEC;
++}
++
++void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++
++ if (type == TYPE_UNUSED)
++ *(ep->name) = 0x0;
++
++ else if (type == TYPE_DELETED)
++ *(ep->name) = 0xE5;
++
++ else if (type == TYPE_EXTEND)
++ ep->attr = ATTR_EXTEND;
++
++ else if (type == TYPE_DIR)
++ ep->attr = ATTR_SUBDIR;
++
++ else if (type == TYPE_FILE)
++ ep->attr = ATTR_ARCHIVE;
++
++ else if (type == TYPE_SYMLINK)
++ ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK;
++}
++
++void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type)
++{
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++
++ if (type == TYPE_UNUSED) {
++ ep->type = 0x0;
++ } else if (type == TYPE_DELETED) {
++ ep->type &= ~0x80;
++ } else if (type == TYPE_STREAM) {
++ ep->type = 0xC0;
++ } else if (type == TYPE_EXTEND) {
++ ep->type = 0xC1;
++ } else if (type == TYPE_BITMAP) {
++ ep->type = 0x81;
++ } else if (type == TYPE_UPCASE) {
++ ep->type = 0x82;
++ } else if (type == TYPE_VOLUME) {
++ ep->type = 0x83;
++ } else if (type == TYPE_DIR) {
++ ep->type = 0x85;
++ SET16_A(ep->attr, ATTR_SUBDIR);
++ } else if (type == TYPE_FILE) {
++ ep->type = 0x85;
++ SET16_A(ep->attr, ATTR_ARCHIVE);
++ } else if (type == TYPE_SYMLINK) {
++ ep->type = 0x85;
++ SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK);
++ }
++}
++
++UINT32 fat_get_entry_attr(DENTRY_T *p_entry)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ return((UINT32) ep->attr);
++}
++
++UINT32 exfat_get_entry_attr(DENTRY_T *p_entry)
++{
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++ return((UINT32) GET16_A(ep->attr));
++}
++
++void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ ep->attr = (UINT8) attr;
++}
++
++void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr)
++{
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++ SET16_A(ep->attr, (UINT16) attr);
++}
++
++UINT8 fat_get_entry_flag(DENTRY_T *p_entry)
++{
++ return 0x01;
++}
++
++UINT8 exfat_get_entry_flag(DENTRY_T *p_entry)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ return(ep->flags);
++}
++
++void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags)
++{
++}
++
++void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ ep->flags = flags;
++}
++
++UINT32 fat_get_entry_clu0(DENTRY_T *p_entry)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ return((GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo));
++}
++
++UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ return(GET32_A(ep->start_clu));
++}
++
++void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu));
++ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16));
++}
++
++void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ SET32_A(ep->start_clu, start_clu);
++}
++
++UINT64 fat_get_entry_size(DENTRY_T *p_entry)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ return((UINT64) GET32_A(ep->size));
++}
++
++UINT64 exfat_get_entry_size(DENTRY_T *p_entry)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ return(GET64_A(ep->valid_size));
++}
++
++void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size)
++{
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++ SET32_A(ep->size, (UINT32) size);
++}
++
++void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size)
++{
++ STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry;
++ SET64_A(ep->valid_size, size);
++ SET64_A(ep->size, size);
++}
++
++void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode)
++{
++ UINT16 t = 0x00, d = 0x21;
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++
++ switch (mode) {
++ case TM_CREATE:
++ t = GET16_A(ep->create_time);
++ d = GET16_A(ep->create_date);
++ break;
++ case TM_MODIFY:
++ t = GET16_A(ep->modify_time);
++ d = GET16_A(ep->modify_date);
++ break;
++ }
++
++ tp->sec = (t & 0x001F) << 1;
++ tp->min = (t >> 5) & 0x003F;
++ tp->hour = (t >> 11);
++ tp->day = (d & 0x001F);
++ tp->mon = (d >> 5) & 0x000F;
++ tp->year = (d >> 9);
++}
++
++void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode)
++{
++ UINT16 t = 0x00, d = 0x21;
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++
++ switch (mode) {
++ case TM_CREATE:
++ t = GET16_A(ep->create_time);
++ d = GET16_A(ep->create_date);
++ break;
++ case TM_MODIFY:
++ t = GET16_A(ep->modify_time);
++ d = GET16_A(ep->modify_date);
++ break;
++ case TM_ACCESS:
++ t = GET16_A(ep->access_time);
++ d = GET16_A(ep->access_date);
++ break;
++ }
++
++ tp->sec = (t & 0x001F) << 1;
++ tp->min = (t >> 5) & 0x003F;
++ tp->hour = (t >> 11);
++ tp->day = (d & 0x001F);
++ tp->mon = (d >> 5) & 0x000F;
++ tp->year = (d >> 9);
++}
++
++void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode)
++{
++ UINT16 t, d;
++ DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry;
++
++ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1);
++ d = (tp->year << 9) | (tp->mon << 5) | tp->day;
++
++ switch (mode) {
++ case TM_CREATE:
++ SET16_A(ep->create_time, t);
++ SET16_A(ep->create_date, d);
++ break;
++ case TM_MODIFY:
++ SET16_A(ep->modify_time, t);
++ SET16_A(ep->modify_date, d);
++ break;
++ }
++}
++
++void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode)
++{
++ UINT16 t, d;
++ FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry;
++
++ t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1);
++ d = (tp->year << 9) | (tp->mon << 5) | tp->day;
++
++ switch (mode) {
++ case TM_CREATE:
++ SET16_A(ep->create_time, t);
++ SET16_A(ep->create_date, d);
++ break;
++ case TM_MODIFY:
++ SET16_A(ep->modify_time, t);
++ SET16_A(ep->modify_date, d);
++ break;
++ case TM_ACCESS:
++ SET16_A(ep->access_time, t);
++ SET16_A(ep->access_date, d);
++ break;
++ }
++}
++
++INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type,
++ UINT32 start_clu, UINT64 size)
++{
++ UINT32 sector;
++ DOS_DENTRY_T *dos_ep;
++
++ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!dos_ep)
++ return FFS_MEDIAERR;
++
++ init_dos_entry(dos_ep, type, start_clu);
++ buf_modify(sb, sector);
++
++ return FFS_SUCCESS;
++}
++
++INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type,
++ UINT32 start_clu, UINT64 size)
++{
++ UINT32 sector;
++ UINT8 flags;
++ FILE_DENTRY_T *file_ep;
++ STRM_DENTRY_T *strm_ep;
++
++ flags = (type == TYPE_FILE) ? 0x01 : 0x03;
++
++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!file_ep)
++ return FFS_MEDIAERR;
++
++ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, &sector);
++ if (!strm_ep)
++ return FFS_MEDIAERR;
++
++ init_file_entry(file_ep, type);
++ buf_modify(sb, sector);
++
++ init_strm_entry(strm_ep, flags, start_clu, size);
++ buf_modify(sb, sector);
++
++ return FFS_SUCCESS;
++}
++
++INT32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries,
++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
++{
++ INT32 i;
++ UINT32 sector;
++ UINT8 chksum;
++ UINT16 *uniname = p_uniname->name;
++ DOS_DENTRY_T *dos_ep;
++ EXT_DENTRY_T *ext_ep;
++
++ dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!dos_ep)
++ return FFS_MEDIAERR;
++
++ dos_ep->lcase = p_dosname->name_case;
++ MEMCPY(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH);
++ buf_modify(sb, sector);
++
++ if ((--num_entries) > 0) {
++ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0);
++
++ for (i = 1; i < num_entries; i++) {
++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, &sector);
++ if (!ext_ep)
++ return FFS_MEDIAERR;
++
++ init_ext_entry(ext_ep, i, chksum, uniname);
++ buf_modify(sb, sector);
++ uniname += 13;
++ }
++
++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, &sector);
++ if (!ext_ep)
++ return FFS_MEDIAERR;
++
++ init_ext_entry(ext_ep, i+0x40, chksum, uniname);
++ buf_modify(sb, sector);
++ }
++
++ return FFS_SUCCESS;
++}
++
++INT32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries,
++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
++{
++ INT32 i;
++ UINT32 sector;
++ UINT16 *uniname = p_uniname->name;
++ FILE_DENTRY_T *file_ep;
++ STRM_DENTRY_T *strm_ep;
++ NAME_DENTRY_T *name_ep;
++
++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!file_ep)
++ return FFS_MEDIAERR;
++
++ file_ep->num_ext = (UINT8)(num_entries - 1);
++ buf_modify(sb, sector);
++
++ strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, &sector);
++ if (!strm_ep)
++ return FFS_MEDIAERR;
++
++ strm_ep->name_len = p_uniname->name_len;
++ SET16_A(strm_ep->name_hash, p_uniname->name_hash);
++ buf_modify(sb, sector);
++
++ for (i = 2; i < num_entries; i++) {
++ name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, &sector);
++ if (!name_ep)
++ return FFS_MEDIAERR;
++
++ init_name_entry(name_ep, uniname);
++ buf_modify(sb, sector);
++ uniname += 15;
++ }
++
++ update_dir_checksum(sb, p_dir, entry);
++
++ return FFS_SUCCESS;
++}
++
++void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu)
++{
++ TIMESTAMP_T tm, *tp;
++
++ fat_set_entry_type((DENTRY_T *) ep, type);
++ SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu));
++ SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16));
++ SET32_A(ep->size, 0);
++
++ tp = tm_current(&tm);
++ fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE);
++ fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY);
++ SET16_A(ep->access_date, 0);
++ ep->create_time_ms = 0;
++}
++
++void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname)
++{
++ INT32 i;
++ UINT8 end = FALSE;
++
++ fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND);
++ ep->order = (UINT8) order;
++ ep->sysid = 0;
++ ep->checksum = chksum;
++ SET16_A(ep->start_clu, 0);
++
++ for (i = 0; i < 10; i += 2) {
++ if (!end) {
++ SET16(ep->unicode_0_4+i, *uniname);
++ if (*uniname == 0x0)
++ end = TRUE;
++ else
++ uniname++;
++ } else {
++ SET16(ep->unicode_0_4+i, 0xFFFF);
++ }
++ }
++
++ for (i = 0; i < 12; i += 2) {
++ if (!end) {
++ SET16_A(ep->unicode_5_10+i, *uniname);
++ if (*uniname == 0x0)
++ end = TRUE;
++ else
++ uniname++;
++ } else {
++ SET16_A(ep->unicode_5_10+i, 0xFFFF);
++ }
++ }
++
++ for (i = 0; i < 4; i += 2) {
++ if (!end) {
++ SET16_A(ep->unicode_11_12+i, *uniname);
++ if (*uniname == 0x0)
++ end = TRUE;
++ else
++ uniname++;
++ } else {
++ SET16_A(ep->unicode_11_12+i, 0xFFFF);
++ }
++ }
++}
++
++void init_file_entry(FILE_DENTRY_T *ep, UINT32 type)
++{
++ TIMESTAMP_T tm, *tp;
++
++ exfat_set_entry_type((DENTRY_T *) ep, type);
++
++ tp = tm_current(&tm);
++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE);
++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY);
++ exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS);
++ ep->create_time_ms = 0;
++ ep->modify_time_ms = 0;
++ ep->access_time_ms = 0;
++}
++
++void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size)
++{
++ exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM);
++ ep->flags = flags;
++ SET32_A(ep->start_clu, start_clu);
++ SET64_A(ep->valid_size, size);
++ SET64_A(ep->size, size);
++}
++
++void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname)
++{
++ INT32 i;
++
++ exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND);
++ ep->flags = 0x0;
++
++ for (i = 0; i < 30; i++, i++) {
++ SET16_A(ep->unicode_0_14+i, *uniname);
++ if (*uniname == 0x0)
++ break;
++ uniname++;
++ }
++}
++
++void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries)
++{
++ INT32 i;
++ UINT32 sector;
++ DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ for (i = num_entries-1; i >= order; i--) {
++ ep = get_entry_in_dir(sb, p_dir, entry-i, &sector);
++ if (!ep)
++ return;
++
++ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
++ buf_modify(sb, sector);
++ }
++}
++
++void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries)
++{
++ INT32 i;
++ UINT32 sector;
++ DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ for (i = order; i < num_entries; i++) {
++ ep = get_entry_in_dir(sb, p_dir, entry+i, &sector);
++ if (!ep)
++ return;
++
++ p_fs->fs_func->set_entry_type(ep, TYPE_DELETED);
++ buf_modify(sb, sector);
++ }
++}
++
++void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry)
++{
++ INT32 i, num_entries;
++ UINT32 sector;
++ UINT16 chksum;
++ FILE_DENTRY_T *file_ep;
++ DENTRY_T *ep;
++
++ file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!file_ep)
++ return;
++
++ buf_lock(sb, sector);
++
++ num_entries = (INT32) file_ep->num_ext + 1;
++ chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY);
++
++ for (i = 1; i < num_entries; i++) {
++ ep = get_entry_in_dir(sb, p_dir, entry+i, NULL);
++ if (!ep) {
++ buf_unlock(sb, sector);
++ return;
++ }
++
++ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT);
++ }
++
++ SET16_A(file_ep->checksum, chksum);
++ buf_modify(sb, sector);
++ buf_unlock(sb, sector);
++}
++
++void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es)
++{
++ DENTRY_T *ep;
++ UINT16 chksum = 0;
++ INT32 chksum_type = CS_DIR_ENTRY, i;
++
++ ep = (DENTRY_T *)&(es->__buf);
++ for (i=0; i < es->num_entries; i++) {
++ PRINTK ("update_dir_checksum_with_entry_set ep %p\n", ep);
++ chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type);
++ ep++;
++ chksum_type = CS_DEFAULT;
++ }
++
++ ep = (DENTRY_T *)&(es->__buf);
++ SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum);
++ write_whole_entry_set(sb, es);
++}
++
++static INT32 _walk_fat_chain (struct super_block *sb, CHAIN_T *p_dir, INT32 byte_offset, UINT32 *clu)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ INT32 clu_offset;
++ UINT32 cur_clu;
++
++ clu_offset = byte_offset >> p_fs->cluster_size_bits;
++ cur_clu = p_dir->dir;
++
++ if (p_dir->flags == 0x03) {
++ cur_clu += clu_offset;
++ } else {
++ while (clu_offset > 0) {
++ if (FAT_read(sb, cur_clu, &cur_clu) == -1)
++ return FFS_MEDIAERR;
++ clu_offset--;
++ }
++ }
++
++ if (clu)
++ *clu = cur_clu;
++ return FFS_SUCCESS;
++}
++INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset)
++{
++ INT32 off, ret;
++ UINT32 clu=0;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ off = entry << DENTRY_SIZE_BITS;
++
++ if (p_dir->dir == CLUSTER_32(0)) {
++ *offset = off & p_bd->sector_size_mask;
++ *sector = off >> p_bd->sector_size_bits;
++ *sector += p_fs->root_start_sector;
++ } else {
++ ret =_walk_fat_chain(sb, p_dir, off, &clu);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ off &= p_fs->cluster_size - 1;
++
++ *offset = off & p_bd->sector_size_mask;
++ *sector = off >> p_bd->sector_size_bits;
++ *sector += START_SECTOR(clu);
++ }
++ return FFS_SUCCESS;
++}
++
++DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset)
++{
++ UINT8 *buf;
++
++ buf = buf_getblk(sb, sector);
++
++ if (buf == NULL)
++ return NULL;
++
++ return((DENTRY_T *)(buf + offset));
++}
++
++DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector)
++{
++ INT32 off;
++ UINT32 sec;
++ UINT8 *buf;
++
++ if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS)
++ return NULL;
++
++ buf = buf_getblk(sb, sec);
++
++ if (buf == NULL)
++ return NULL;
++
++ if (sector != NULL)
++ *sector = sec;
++ return((DENTRY_T *)(buf + off));
++}
++
++#define ES_MODE_STARTED 0
++#define ES_MODE_GET_FILE_ENTRY 1
++#define ES_MODE_GET_STRM_ENTRY 2
++#define ES_MODE_GET_NAME_ENTRY 3
++#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4
++ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep)
++{
++ INT32 off, ret, byte_offset;
++ UINT32 clu=0;
++ UINT32 sec, entry_type;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ ENTRY_SET_CACHE_T *es = NULL;
++ DENTRY_T *ep, *pos;
++ UINT8 *buf;
++ UINT8 num_entries;
++ INT32 mode = ES_MODE_STARTED;
++
++ PRINTK("get_entry_set_in_dir entered\n");
++ PRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size);
++
++ byte_offset = entry << DENTRY_SIZE_BITS;
++ ret =_walk_fat_chain(sb, p_dir, byte_offset, &clu);
++ if (ret != FFS_SUCCESS)
++ return NULL;
++
++
++ byte_offset &= p_fs->cluster_size - 1;
++
++ off = byte_offset & p_bd->sector_size_mask;
++ sec = byte_offset >> p_bd->sector_size_bits;
++ sec += START_SECTOR(clu);
++
++ buf = buf_getblk(sb, sec);
++ if (buf == NULL)
++ goto err_out;
++
++
++ ep = (DENTRY_T *)(buf + off);
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if ((entry_type != TYPE_FILE)
++ && (entry_type != TYPE_DIR))
++ goto err_out;
++
++ if (type == ES_ALL_ENTRIES)
++ num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1;
++ else
++ num_entries = type;
++
++ PRINTK("trying to malloc %x bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries);
++ es = MALLOC(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T));
++ if (es == NULL)
++ goto err_out;
++
++ es->num_entries = num_entries;
++ es->sector = sec;
++ es->offset = off;
++ es->alloc_flag = p_dir->flags;
++
++ pos = (DENTRY_T *) &(es->__buf);
++
++ while(num_entries) {
++
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED))
++ goto err_out;
++
++ switch(mode) {
++ case ES_MODE_STARTED:
++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR))
++ mode = ES_MODE_GET_FILE_ENTRY;
++ else
++ goto err_out;
++ break;
++ case ES_MODE_GET_FILE_ENTRY:
++ if (entry_type == TYPE_STREAM)
++ mode = ES_MODE_GET_STRM_ENTRY;
++ else
++ goto err_out;
++ break;
++ case ES_MODE_GET_STRM_ENTRY:
++ if (entry_type == TYPE_EXTEND)
++ mode = ES_MODE_GET_NAME_ENTRY;
++ else
++ goto err_out;
++ break;
++ case ES_MODE_GET_NAME_ENTRY:
++ if (entry_type == TYPE_EXTEND)
++ break;
++ else if (entry_type == TYPE_STREAM)
++ goto err_out;
++ else if (entry_type & TYPE_CRITICAL_SEC)
++ mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
++ else
++ goto err_out;
++ break;
++ case ES_MODE_GET_CRITICAL_SEC_ENTRY:
++ if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM))
++ goto err_out;
++ else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC)
++ goto err_out;
++ break;
++ }
++
++ COPY_DENTRY(pos, ep);
++
++ if (--num_entries == 0)
++ break;
++
++ if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) {
++ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
++ if (es->alloc_flag == 0x03) {
++ clu++;
++ } else {
++ if (FAT_read(sb, clu, &clu) == -1)
++ goto err_out;
++ }
++ sec = START_SECTOR(clu);
++ } else {
++ sec++;
++ }
++ buf = buf_getblk(sb, sec);
++ if (buf == NULL)
++ goto err_out;
++ off = 0;
++ ep = (DENTRY_T *)(buf);
++ } else {
++ ep++;
++ off += DENTRY_SIZE;
++ }
++ pos++;
++ }
++
++ if (file_ep)
++ *file_ep = (DENTRY_T *)&(es->__buf);
++
++ PRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n",
++ es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf));
++ PRINTK("get_entry_set_in_dir exited %p\n", es);
++ return es;
++err_out:
++ PRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es);
++ if (es)
++ FREE(es);
++ return NULL;
++}
++
++void release_entry_set (ENTRY_SET_CACHE_T *es)
++{
++ PRINTK("release_entry_set %p\n", es);
++ FREE(es);
++}
++
++
++static INT32 __write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, UINT32 sec, INT32 off, UINT32 count)
++{
++ INT32 num_entries, buf_off = (off - es->offset);
++ UINT32 remaining_byte_in_sector, copy_entries;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ UINT32 clu;
++ UINT8 *buf, *esbuf = (UINT8 *)&(es->__buf);
++
++ PRINTK("__write_partial_entries_in_entry_set entered\n");
++ PRINTK("es %p sec %u off %d count %d\n", es, sec, off, count);
++ num_entries = count;
++
++ while(num_entries) {
++ remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off;
++ copy_entries = MIN(remaining_byte_in_sector>> DENTRY_SIZE_BITS , num_entries);
++ buf = buf_getblk(sb, sec);
++ if (buf == NULL)
++ goto err_out;
++ PRINTK("es->buf %p buf_off %u\n", esbuf, buf_off);
++ PRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec);
++ MEMCPY(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS);
++ buf_modify(sb, sec);
++ num_entries -= copy_entries;
++
++ if (num_entries) {
++ if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
++ clu = GET_CLUSTER_FROM_SECTOR(sec);
++ if (es->alloc_flag == 0x03) {
++ clu++;
++ } else {
++ if (FAT_read(sb, clu, &clu) == -1)
++ goto err_out;
++ }
++ sec = START_SECTOR(clu);
++ } else {
++ sec++;
++ }
++ off = 0;
++ buf_off += copy_entries << DENTRY_SIZE_BITS;
++ }
++ }
++
++ PRINTK("__write_partial_entries_in_entry_set exited successfully\n");
++ return FFS_SUCCESS;
++err_out:
++ PRINTK("__write_partial_entries_in_entry_set failed\n");
++ return FFS_ERROR;
++}
++
++INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es)
++{
++ return (__write_partial_entries_in_entry_set(sb, es, es->sector,es->offset, es->num_entries));
++}
++
++INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count)
++{
++ INT32 ret, byte_offset, off;
++ UINT32 clu=0, sec;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ CHAIN_T dir;
++
++ if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries)
++ return FFS_ERROR;
++
++ dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector);
++ dir.flags = es->alloc_flag;
++ dir.size = 0xffffffff;
++
++ byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits;
++ byte_offset += ((INT32)ep - (INT32)&(es->__buf)) + es->offset;
++
++ ret =_walk_fat_chain(sb, &dir, byte_offset, &clu);
++ if (ret != FFS_SUCCESS)
++ return ret;
++ byte_offset &= p_fs->cluster_size - 1;
++ off = byte_offset & p_bd->sector_size_mask;
++ sec = byte_offset >> p_bd->sector_size_bits;
++ sec += START_SECTOR(clu);
++ return (__write_partial_entries_in_entry_set(sb, es, sec, off, count));
++}
++
++INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries)
++{
++ INT32 i, dentry, num_empty = 0;
++ INT32 dentries_per_clu;
++ UINT32 type;
++ CHAIN_T clu;
++ DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ if (p_fs->hint_uentry.dir == p_dir->dir) {
++ if (p_fs->hint_uentry.entry == -1)
++ return -1;
++
++ clu.dir = p_fs->hint_uentry.clu.dir;
++ clu.size = p_fs->hint_uentry.clu.size;
++ clu.flags = p_fs->hint_uentry.clu.flags;
++
++ dentry = p_fs->hint_uentry.entry;
++ } else {
++ p_fs->hint_uentry.entry = -1;
++
++ clu.dir = p_dir->dir;
++ clu.size = p_dir->size;
++ clu.flags = p_dir->flags;
++
++ dentry = 0;
++ }
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ if (p_dir->dir == CLUSTER_32(0))
++ i = dentry % dentries_per_clu;
++ else
++ i = dentry & (dentries_per_clu-1);
++
++ for ( ; i < dentries_per_clu; i++, dentry++) {
++ ep = get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return -1;
++
++ type = p_fs->fs_func->get_entry_type(ep);
++
++ if (type == TYPE_UNUSED) {
++ num_empty++;
++ if (p_fs->hint_uentry.entry == -1) {
++ p_fs->hint_uentry.dir = p_dir->dir;
++ p_fs->hint_uentry.entry = dentry;
++
++ p_fs->hint_uentry.clu.dir = clu.dir;
++ p_fs->hint_uentry.clu.size = clu.size;
++ p_fs->hint_uentry.clu.flags = clu.flags;
++ }
++ } else if (type == TYPE_DELETED) {
++ num_empty++;
++ } else {
++ num_empty = 0;
++ }
++
++ if (num_empty >= num_entries) {
++ p_fs->hint_uentry.dir = CLUSTER_32(~0);
++ p_fs->hint_uentry.entry = -1;
++
++ if (p_fs->vol_type == EXFAT)
++ return(dentry - (num_entries-1));
++ else
++ return(dentry);
++ }
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (clu.flags == 0x03) {
++ if ((--clu.size) > 0)
++ clu.dir++;
++ else
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return -1;
++ }
++ }
++
++ return -1;
++}
++
++INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries)
++{
++ INT32 ret, dentry;
++ UINT32 last_clu, sector;
++ UINT64 size = 0;
++ CHAIN_T clu;
++ DENTRY_T *ep = NULL;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++
++ if (p_dir->dir == CLUSTER_32(0))
++ return(search_deleted_or_unused_entry(sb, p_dir, num_entries));
++
++ while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) {
++ if (p_fs->dev_ejected)
++ break;
++
++ if (p_fs->vol_type == EXFAT) {
++ if (p_dir->dir != p_fs->root_dir) {
++ size = i_size_read(inode);
++ }
++ }
++
++ last_clu = find_last_cluster(sb, p_dir);
++ clu.dir = last_clu + 1;
++ clu.size = 0;
++ clu.flags = p_dir->flags;
++
++ ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu);
++ if (ret < 1)
++ return -1;
++
++ if (clear_cluster(sb, clu.dir) != FFS_SUCCESS)
++ return -1;
++
++ if (clu.flags != p_dir->flags) {
++ exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size);
++ p_dir->flags = 0x01;
++ p_fs->hint_uentry.clu.flags = 0x01;
++ }
++ if (clu.flags == 0x01)
++ FAT_write(sb, last_clu, clu.dir);
++
++ if (p_fs->hint_uentry.entry == -1) {
++ p_fs->hint_uentry.dir = p_dir->dir;
++ p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
++
++ p_fs->hint_uentry.clu.dir = clu.dir;
++ p_fs->hint_uentry.clu.size = 0;
++ p_fs->hint_uentry.clu.flags = clu.flags;
++ }
++ p_fs->hint_uentry.clu.size++;
++ p_dir->size++;
++
++ if (p_fs->vol_type == EXFAT) {
++ if (p_dir->dir != p_fs->root_dir) {
++ size += p_fs->cluster_size;
++
++ ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, &sector);
++ if (!ep)
++ return -1;
++ p_fs->fs_func->set_entry_size(ep, size);
++ p_fs->fs_func->set_entry_flag(ep, p_dir->flags);
++ buf_modify(sb, sector);
++
++ update_dir_checksum(sb, &(fid->dir), fid->entry);
++ }
++ }
++
++ i_size_write(inode, i_size_read(inode)+p_fs->cluster_size);
++ EXFAT_I(inode)->mmu_private += p_fs->cluster_size;
++ EXFAT_I(inode)->fid.size += p_fs->cluster_size;
++ EXFAT_I(inode)->fid.flags = p_dir->flags;
++ inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9);
++ }
++
++ return(dentry);
++}
++
++INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type)
++{
++ INT32 i, dentry = 0, lossy = FALSE, len;
++ INT32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE;
++ INT32 dentries_per_clu;
++ UINT32 entry_type;
++ UINT16 entry_uniname[14], *uniname = NULL, unichar;
++ CHAIN_T clu;
++ DENTRY_T *ep;
++ DOS_DENTRY_T *dos_ep;
++ EXT_DENTRY_T *ext_ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_dir->dir == p_fs->root_dir) {
++ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) ||
++ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME)))
++ return -1;
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ clu.dir = p_dir->dir;
++ clu.flags = p_dir->flags;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < dentries_per_clu; i++, dentry++) {
++ ep = get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return -2;
++
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
++ if ((type == TYPE_ALL) || (type == entry_type)) {
++ if (is_feasible_entry && has_ext_entry)
++ return(dentry);
++
++ dos_ep = (DOS_DENTRY_T *) ep;
++ if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name)))
++ return(dentry);
++ }
++ is_feasible_entry = TRUE;
++ has_ext_entry = FALSE;
++ } else if (entry_type == TYPE_EXTEND) {
++ if (is_feasible_entry) {
++ ext_ep = (EXT_DENTRY_T *) ep;
++ if (ext_ep->order > 0x40) {
++ order = (INT32)(ext_ep->order - 0x40);
++ uniname = p_uniname->name + 13 * (order-1);
++ } else {
++ order = (INT32) ext_ep->order;
++ uniname -= 13;
++ }
++
++ len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order);
++
++ unichar = *(uniname+len);
++ *(uniname+len) = 0x0;
++
++ if (nls_uniname_cmp(sb, uniname, entry_uniname)) {
++ is_feasible_entry = FALSE;
++ }
++
++ *(uniname+len) = unichar;
++ }
++ has_ext_entry = TRUE;
++ } else if (entry_type == TYPE_UNUSED) {
++ return -2;
++ } else {
++ is_feasible_entry = TRUE;
++ has_ext_entry = FALSE;
++ }
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return -2;
++ }
++
++ return -2;
++}
++
++INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type)
++{
++ INT32 i, dentry = 0, num_ext_entries = 0, len;
++ INT32 order = 0, is_feasible_entry = FALSE;
++ INT32 dentries_per_clu, num_empty = 0;
++ UINT32 entry_type;
++ UINT16 entry_uniname[16], *uniname = NULL, unichar;
++ CHAIN_T clu;
++ DENTRY_T *ep;
++ FILE_DENTRY_T *file_ep;
++ STRM_DENTRY_T *strm_ep;
++ NAME_DENTRY_T *name_ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_dir->dir == p_fs->root_dir) {
++ if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) ||
++ (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME)))
++ return -1;
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ clu.dir = p_dir->dir;
++ clu.size = p_dir->size;
++ clu.flags = p_dir->flags;
++
++ p_fs->hint_uentry.dir = p_dir->dir;
++ p_fs->hint_uentry.entry = -1;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < dentries_per_clu; i++, dentry++) {
++ ep = get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return -2;
++
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) {
++ is_feasible_entry = FALSE;
++
++ if (p_fs->hint_uentry.entry == -1) {
++ num_empty++;
++
++ if (num_empty == 1) {
++ p_fs->hint_uentry.clu.dir = clu.dir;
++ p_fs->hint_uentry.clu.size = clu.size;
++ p_fs->hint_uentry.clu.flags = clu.flags;
++ }
++ if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) {
++ p_fs->hint_uentry.entry = dentry - (num_empty-1);
++ }
++ }
++
++ if (entry_type == TYPE_UNUSED) {
++ return -2;
++ }
++ } else {
++ num_empty = 0;
++
++ if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
++ if ((type == TYPE_ALL) || (type == entry_type)) {
++ file_ep = (FILE_DENTRY_T *) ep;
++ num_ext_entries = file_ep->num_ext;
++ is_feasible_entry = TRUE;
++ } else {
++ is_feasible_entry = FALSE;
++ }
++ } else if (entry_type == TYPE_STREAM) {
++ if (is_feasible_entry) {
++ strm_ep = (STRM_DENTRY_T *) ep;
++ if ((p_uniname->name_hash == GET16_A(strm_ep->name_hash)) &&
++ (p_uniname->name_len == strm_ep->name_len)) {
++ order = 1;
++ } else {
++ is_feasible_entry = FALSE;
++ }
++ }
++ } else if (entry_type == TYPE_EXTEND) {
++ if (is_feasible_entry) {
++ name_ep = (NAME_DENTRY_T *) ep;
++
++ if ((++order) == 2)
++ uniname = p_uniname->name;
++ else
++ uniname += 15;
++
++ len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order);
++
++ unichar = *(uniname+len);
++ *(uniname+len) = 0x0;
++
++ if (nls_uniname_cmp(sb, uniname, entry_uniname)) {
++ is_feasible_entry = FALSE;
++ } else if (order == num_ext_entries) {
++ p_fs->hint_uentry.dir = CLUSTER_32(~0);
++ p_fs->hint_uentry.entry = -1;
++ return(dentry - (num_ext_entries));
++ }
++
++ *(uniname+len) = unichar;
++ }
++ } else {
++ is_feasible_entry = FALSE;
++ }
++ }
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (clu.flags == 0x03) {
++ if ((--clu.size) > 0)
++ clu.dir++;
++ else
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return -2;
++ }
++ }
++
++ return -2;
++}
++
++INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry)
++{
++ INT32 count = 0;
++ UINT8 chksum;
++ DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry;
++ EXT_DENTRY_T *ext_ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0);
++
++ for (entry--; entry >= 0; entry--) {
++ ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL);
++ if (!ext_ep)
++ return -1;
++
++ if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) &&
++ (ext_ep->checksum == chksum)) {
++ count++;
++ if (ext_ep->order > 0x40)
++ return(count);
++ } else {
++ return(count);
++ }
++ }
++
++ return(count);
++}
++
++INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry)
++{
++ INT32 i, count = 0;
++ UINT32 type;
++ FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry;
++ DENTRY_T *ext_ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) {
++ ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL);
++ if (!ext_ep)
++ return -1;
++
++ type = p_fs->fs_func->get_entry_type(ext_ep);
++ if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) {
++ count++;
++ } else {
++ return(count);
++ }
++ }
++
++ return(count);
++}
++
++INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type)
++{
++ INT32 i, count = 0;
++ INT32 dentries_per_clu;
++ UINT32 entry_type;
++ CHAIN_T clu;
++ DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ clu.dir = p_dir->dir;
++ clu.size = p_dir->size;
++ clu.flags = p_dir->flags;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < dentries_per_clu; i++) {
++ ep = get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return -1;
++
++ entry_type = p_fs->fs_func->get_entry_type(ep);
++
++ if (entry_type == TYPE_UNUSED)
++ return(count);
++ if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI))
++ continue;
++
++ if ((type == TYPE_ALL) || (type == entry_type))
++ count++;
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (clu.flags == 0x03) {
++ if ((--clu.size) > 0)
++ clu.dir++;
++ else
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return -1;
++ }
++ }
++
++ return(count);
++}
++
++BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir)
++{
++ INT32 i, count = 0;
++ INT32 dentries_per_clu;
++ UINT32 type;
++ CHAIN_T clu;
++ DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ clu.dir = p_dir->dir;
++ clu.size = p_dir->size;
++ clu.flags = p_dir->flags;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < dentries_per_clu; i++) {
++ ep = get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ break;
++
++ type = p_fs->fs_func->get_entry_type(ep);
++
++ if (type == TYPE_UNUSED)
++ return TRUE;
++ if ((type != TYPE_FILE) && (type != TYPE_DIR))
++ continue;
++
++ if (p_dir->dir == CLUSTER_32(0)) {
++ return FALSE;
++ } else {
++ if (p_fs->vol_type == EXFAT)
++ return FALSE;
++ if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2))
++ return FALSE;
++ }
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (clu.flags == 0x03) {
++ if ((--clu.size) > 0)
++ clu.dir++;
++ else
++ clu.dir = CLUSTER_32(~0);
++ } else {
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ break;
++ }
++ }
++
++ return TRUE;
++}
++
++INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname)
++{
++ INT32 ret, num_entries, lossy = FALSE;
++ INT8 **r;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ num_entries = p_fs->fs_func->calc_num_entries(p_uniname);
++ if (num_entries == 0)
++ return FFS_INVALIDPATH;
++
++ if (p_fs->vol_type != EXFAT) {
++ nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy);
++
++ if (lossy) {
++ ret = fat_generate_dos_name(sb, p_dir, p_dosname);
++ if (ret)
++ return ret;
++ } else {
++ for (r = reserved_names; *r; r++) {
++ if (!STRNCMP((void *) p_dosname->name, *r, 8))
++ return FFS_INVALIDPATH;
++ }
++
++ if (p_dosname->name_case != 0xFF)
++ num_entries = 1;
++ }
++
++ if (num_entries > 1)
++ p_dosname->name_case = 0x0;
++ }
++
++ *entries = num_entries;
++
++ return FFS_SUCCESS;
++}
++
++void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode)
++{
++ DOS_NAME_T dos_name;
++
++ if (mode == 0x0)
++ dos_name.name_case = 0x0;
++ else
++ dos_name.name_case = ep->lcase;
++
++ MEMCPY(dos_name.name, ep->name, DOS_NAME_LENGTH);
++ nls_dosname_to_uniname(sb, p_uniname, &dos_name);
++}
++
++void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname)
++{
++ INT32 i;
++ EXT_DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ for (entry--, i = 1; entry >= 0; entry--, i++) {
++ ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL);
++ if (!ep)
++ return;
++
++ if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) {
++ extract_uni_name_from_ext_entry(ep, uniname, i);
++ if (ep->order > 0x40)
++ return;
++ } else {
++ return;
++ }
++
++ uniname += 13;
++ }
++}
++
++void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname)
++{
++ INT32 i;
++ DENTRY_T *ep;
++ ENTRY_SET_CACHE_T *es;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep);
++ if (es == NULL || es->num_entries < 3) {
++ if(es) {
++ release_entry_set(es);
++ }
++ return;
++ }
++
++ ep += 2;
++
++ for (i = 2; i < es->num_entries; i++, ep++) {
++ if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) {
++ extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i);
++ } else {
++ goto out;
++ }
++ uniname += 15;
++ }
++
++out:
++ release_entry_set(es);
++}
++
++INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order)
++{
++ INT32 i, len = 0;
++
++ for (i = 0; i < 10; i += 2) {
++ *uniname = GET16(ep->unicode_0_4+i);
++ if (*uniname == 0x0)
++ return(len);
++ uniname++;
++ len++;
++ }
++
++ if (order < 20) {
++ for (i = 0; i < 12; i += 2) {
++ *uniname = GET16_A(ep->unicode_5_10+i);
++ if (*uniname == 0x0)
++ return(len);
++ uniname++;
++ len++;
++ }
++ } else {
++ for (i = 0; i < 8; i += 2) {
++ *uniname = GET16_A(ep->unicode_5_10+i);
++ if (*uniname == 0x0)
++ return(len);
++ uniname++;
++ len++;
++ }
++ *uniname = 0x0;
++ return(len);
++ }
++
++ for (i = 0; i < 4; i += 2) {
++ *uniname = GET16_A(ep->unicode_11_12+i);
++ if (*uniname == 0x0)
++ return(len);
++ uniname++;
++ len++;
++ }
++
++ *uniname = 0x0;
++ return(len);
++
++}
++
++INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order)
++{
++ INT32 i, len = 0;
++
++ for (i = 0; i < 30; i += 2) {
++ *uniname = GET16_A(ep->unicode_0_14+i);
++ if (*uniname == 0x0)
++ return(len);
++ uniname++;
++ len++;
++ }
++
++ *uniname = 0x0;
++ return(len);
++
++}
++
++INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname)
++{
++ INT32 i, j, count = 0, count_begin = FALSE;
++ INT32 dentries_per_clu;
++ UINT32 type;
++ UINT8 bmap[128];
++ CHAIN_T clu;
++ DOS_DENTRY_T *ep;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ Bitmap_clear_all(bmap, 128);
++ Bitmap_set(bmap, 0);
++
++ if (p_dir->dir == CLUSTER_32(0))
++ dentries_per_clu = p_fs->dentries_in_root;
++ else
++ dentries_per_clu = p_fs->dentries_per_clu;
++
++ clu.dir = p_dir->dir;
++ clu.flags = p_dir->flags;
++
++ while (clu.dir != CLUSTER_32(~0)) {
++ if (p_fs->dev_ejected)
++ break;
++
++ for (i = 0; i < dentries_per_clu; i++) {
++ ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL);
++ if (!ep)
++ return FFS_MEDIAERR;
++
++ type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep);
++
++ if (type == TYPE_UNUSED)
++ break;
++ if ((type != TYPE_FILE) && (type != TYPE_DIR))
++ continue;
++
++ count = 0;
++ count_begin = FALSE;
++
++ for (j = 0; j < 8; j++) {
++ if (ep->name[j] == ' ')
++ break;
++
++ if (ep->name[j] == '~') {
++ count_begin = TRUE;
++ } else if (count_begin) {
++ if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) {
++ count = count * 10 + (ep->name[j] - '0');
++ } else {
++ count = 0;
++ count_begin = FALSE;
++ }
++ }
++ }
++
++ if ((count > 0) && (count < 1024))
++ Bitmap_set(bmap, count);
++ }
++
++ if (p_dir->dir == CLUSTER_32(0))
++ break;
++
++ if (FAT_read(sb, clu.dir, &(clu.dir)) != 0)
++ return FFS_MEDIAERR;
++ }
++
++ count = 0;
++ for (i = 0; i < 128; i++) {
++ if (bmap[i] != 0xFF) {
++ for (j = 0; j < 8; j++) {
++ if (Bitmap_test(&(bmap[i]), j) == 0) {
++ count = (i << 3) + j;
++ break;
++ }
++ }
++ if (count != 0)
++ break;
++ }
++ }
++
++ if ((count == 0) || (count >= 1024))
++ return FFS_FILEEXIST;
++ else
++ fat_attach_count_to_dos_name(p_dosname->name, count);
++
++ return FFS_SUCCESS;
++}
++
++void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count)
++{
++ INT32 i, j, length;
++ INT8 str_count[6];
++
++ str_count[0] = '~';
++ str_count[1] = '\0';
++ my_itoa(&(str_count[1]), count);
++ length = STRLEN(str_count);
++
++ i = j = 0;
++ while (j <= (8 - length)) {
++ i = j;
++ if (dosname[j] == ' ')
++ break;
++ if (dosname[j] & 0x80)
++ j += 2;
++ else
++ j++;
++ }
++
++ for (j = 0; j < length; i++, j++)
++ dosname[i] = (UINT8) str_count[j];
++
++ if (i == 7)
++ dosname[7] = ' ';
++
++}
++
++INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname)
++{
++ INT32 len;
++
++ len = p_uniname->name_len;
++ if (len == 0)
++ return 0;
++
++ return((len-1) / 13 + 2);
++
++}
++
++INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname)
++{
++ INT32 len;
++
++ len = p_uniname->name_len;
++ if (len == 0)
++ return 0;
++
++ return((len-1) / 15 + 3);
++
++}
++
++UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum)
++{
++ INT32 i;
++ UINT8 *c = (UINT8 *) data;
++
++ for (i = 0; i < len; i++, c++)
++ chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c;
++
++ return(chksum);
++}
++
++UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type)
++{
++ INT32 i;
++ UINT8 *c = (UINT8 *) data;
++
++ switch (type) {
++ case CS_DIR_ENTRY:
++ for (i = 0; i < len; i++, c++) {
++ if ((i == 2) || (i == 3))
++ continue;
++ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c;
++ }
++ break;
++ default
++ :
++ for (i = 0; i < len; i++, c++) {
++ chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c;
++ }
++ }
++
++ return(chksum);
++}
++
++UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type)
++{
++ INT32 i;
++ UINT8 *c = (UINT8 *) data;
++
++ switch (type) {
++ case CS_PBR_SECTOR:
++ for (i = 0; i < len; i++, c++) {
++ if ((i == 106) || (i == 107) || (i == 112))
++ continue;
++ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c;
++ }
++ break;
++ default
++ :
++ for (i = 0; i < len; i++, c++) {
++ chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c;
++ }
++ }
++
++ return(chksum);
++}
++
++
++INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname)
++{
++ INT32 lossy = FALSE;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++
++ if (STRLEN(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE))
++ return(FFS_INVALIDPATH);
++
++ STRCPY(name_buf, path);
++
++ nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy);
++ if (lossy)
++ return(FFS_INVALIDPATH);
++
++ fid->size = i_size_read(inode);
++
++ p_dir->dir = fid->start_clu;
++ p_dir->size = (INT32)(fid->size >> p_fs->cluster_size_bits);
++ p_dir->flags = fid->flags;
++
++ return(FFS_SUCCESS);
++}
++
++
++static FS_FUNC_T fat_fs_func = {
++ .alloc_cluster = fat_alloc_cluster,
++ .free_cluster = fat_free_cluster,
++ .count_used_clusters = fat_count_used_clusters,
++
++ .init_dir_entry = fat_init_dir_entry,
++ .init_ext_entry = fat_init_ext_entry,
++ .find_dir_entry = fat_find_dir_entry,
++ .delete_dir_entry = fat_delete_dir_entry,
++ .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry,
++ .count_ext_entries = fat_count_ext_entries,
++ .calc_num_entries = fat_calc_num_entries,
++
++ .get_entry_type = fat_get_entry_type,
++ .set_entry_type = fat_set_entry_type,
++ .get_entry_attr = fat_get_entry_attr,
++ .set_entry_attr = fat_set_entry_attr,
++ .get_entry_flag = fat_get_entry_flag,
++ .set_entry_flag = fat_set_entry_flag,
++ .get_entry_clu0 = fat_get_entry_clu0,
++ .set_entry_clu0 = fat_set_entry_clu0,
++ .get_entry_size = fat_get_entry_size,
++ .set_entry_size = fat_set_entry_size,
++ .get_entry_time = fat_get_entry_time,
++ .set_entry_time = fat_set_entry_time,
++};
++
++
++INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr)
++{
++ INT32 num_reserved, num_root_sectors;
++ BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_bpb->num_fats == 0)
++ return FFS_FORMATERR;
++
++ num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS;
++ num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1;
++
++ p_fs->sectors_per_clu = p_bpb->sectors_per_clu;
++ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu);
++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits;
++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
++
++ p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors);
++
++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved);
++ if (p_bpb->num_fats == 1)
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
++ else
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors;
++
++ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors;
++ p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors;
++
++ p_fs->num_sectors = GET16(p_bpb->num_sectors);
++ if (p_fs->num_sectors == 0)
++ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors);
++
++ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector;
++ p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2;
++
++ if (p_fs->num_clusters < FAT12_THRESHOLD)
++ p_fs->vol_type = FAT12;
++ else
++ p_fs->vol_type = FAT16;
++ p_fs->vol_id = GET32(p_bpb->vol_serial);
++
++ p_fs->root_dir = 0;
++ p_fs->dentries_in_root = GET16(p_bpb->num_root_entries);
++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
++
++ p_fs->vol_flag = VOL_CLEAN;
++ p_fs->clu_srch_ptr = 2;
++ p_fs->used_clusters = (UINT32) ~0;
++
++ p_fs->fs_func = &fat_fs_func;
++
++ return FFS_SUCCESS;
++}
++
++INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr)
++{
++ INT32 num_reserved;
++ BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_bpb->num_fats == 0)
++ return FFS_FORMATERR;
++
++ p_fs->sectors_per_clu = p_bpb->sectors_per_clu;
++ p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu);
++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits;
++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
++
++ p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors);
++
++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved);
++ if (p_bpb->num_fats == 1)
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
++ else
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors;
++
++ p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors;
++ p_fs->data_start_sector = p_fs->root_start_sector;
++
++ p_fs->num_sectors = GET32(p_bpb->num_huge_sectors);
++ num_reserved = p_fs->data_start_sector - p_fs->PBR_sector;
++
++ p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2;
++
++ p_fs->vol_type = FAT32;
++ p_fs->vol_id = GET32(p_bpb->vol_serial);
++
++ p_fs->root_dir = GET32(p_bpb->root_cluster);
++ p_fs->dentries_in_root = 0;
++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
++
++ p_fs->vol_flag = VOL_CLEAN;
++ p_fs->clu_srch_ptr = 2;
++ p_fs->used_clusters = (UINT32) ~0;
++
++ p_fs->fs_func = &fat_fs_func;
++
++ return FFS_SUCCESS;
++}
++
++static FS_FUNC_T exfat_fs_func = {
++ .alloc_cluster = exfat_alloc_cluster,
++ .free_cluster = exfat_free_cluster,
++ .count_used_clusters = exfat_count_used_clusters,
++
++ .init_dir_entry = exfat_init_dir_entry,
++ .init_ext_entry = exfat_init_ext_entry,
++ .find_dir_entry = exfat_find_dir_entry,
++ .delete_dir_entry = exfat_delete_dir_entry,
++ .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry,
++ .count_ext_entries = exfat_count_ext_entries,
++ .calc_num_entries = exfat_calc_num_entries,
++
++ .get_entry_type = exfat_get_entry_type,
++ .set_entry_type = exfat_set_entry_type,
++ .get_entry_attr = exfat_get_entry_attr,
++ .set_entry_attr = exfat_set_entry_attr,
++ .get_entry_flag = exfat_get_entry_flag,
++ .set_entry_flag = exfat_set_entry_flag,
++ .get_entry_clu0 = exfat_get_entry_clu0,
++ .set_entry_clu0 = exfat_set_entry_clu0,
++ .get_entry_size = exfat_get_entry_size,
++ .set_entry_size = exfat_set_entry_size,
++ .get_entry_time = exfat_get_entry_time,
++ .set_entry_time = exfat_set_entry_time,
++};
++
++INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr)
++{
++ BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_bpb->num_fats == 0)
++ return FFS_FORMATERR;
++
++ p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits;
++ p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits;
++ p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits;
++ p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
++
++ p_fs->num_FAT_sectors = GET32(p_bpb->fat_length);
++
++ p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset);
++ if (p_bpb->num_fats == 1)
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
++ else
++ p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors;
++
++ p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset);
++ p_fs->data_start_sector = p_fs->root_start_sector;
++
++ p_fs->num_sectors = GET64(p_bpb->vol_length);
++ p_fs->num_clusters = GET32(p_bpb->clu_count) + 2;
++
++ p_fs->vol_type = EXFAT;
++ p_fs->vol_id = GET32(p_bpb->vol_serial);
++
++ p_fs->root_dir = GET32(p_bpb->root_cluster);
++ p_fs->dentries_in_root = 0;
++ p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
++
++ p_fs->vol_flag = (UINT32) GET16(p_bpb->vol_flags);
++ p_fs->clu_srch_ptr = 2;
++ p_fs->used_clusters = (UINT32) ~0;
++
++ p_fs->fs_func = &exfat_fs_func;
++
++ return FFS_SUCCESS;
++}
++
++INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
++{
++ INT32 ret, dentry, num_entries;
++ UINT64 size;
++ CHAIN_T clu;
++ DOS_NAME_T dos_name, dot_name;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name);
++ if (ret)
++ return ret;
++
++ dentry = find_empty_entry(inode, p_dir, num_entries);
++ if (dentry < 0)
++ return FFS_FULL;
++
++ clu.dir = CLUSTER_32(~0);
++ clu.size = 0;
++ clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
++
++ ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu);
++ if (ret < 1)
++ return FFS_FULL;
++
++ ret = clear_cluster(sb, clu.dir);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ if (p_fs->vol_type == EXFAT) {
++ size = p_fs->cluster_size;
++ } else {
++ size = 0;
++
++ dot_name.name_case = 0x0;
++ MEMCPY(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH);
++
++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ MEMCPY(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH);
++
++ if (p_dir->dir == p_fs->root_dir)
++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0);
++ else
++ ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0);
++
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++ }
++
++ ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ fid->dir.dir = p_dir->dir;
++ fid->dir.size = p_dir->size;
++ fid->dir.flags = p_dir->flags;
++ fid->entry = dentry;
++
++ fid->attr = ATTR_SUBDIR;
++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
++ fid->size = size;
++ fid->start_clu = clu.dir;
++
++ fid->type= TYPE_DIR;
++ fid->rwoffset = 0;
++ fid->hint_last_off = -1;
++
++ return FFS_SUCCESS;
++}
++
++INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid)
++{
++ INT32 ret, dentry, num_entries;
++ DOS_NAME_T dos_name;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name);
++ if (ret)
++ return ret;
++
++ dentry = find_empty_entry(inode, p_dir, num_entries);
++ if (dentry < 0)
++ return FFS_FULL;
++
++ ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ fid->dir.dir = p_dir->dir;
++ fid->dir.size = p_dir->size;
++ fid->dir.flags = p_dir->flags;
++ fid->entry = dentry;
++
++ fid->attr = ATTR_ARCHIVE | mode;
++ fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
++ fid->size = 0;
++ fid->start_clu = CLUSTER_32(~0);
++
++ fid->type= TYPE_FILE;
++ fid->rwoffset = 0;
++ fid->hint_last_off = -1;
++
++ return FFS_SUCCESS;
++}
++
++void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry)
++{
++ INT32 num_entries;
++ UINT32 sector;
++ DENTRY_T *ep;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ ep = get_entry_in_dir(sb, p_dir, entry, &sector);
++ if (!ep)
++ return;
++
++ buf_lock(sb, sector);
++
++ num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep);
++ if (num_entries < 0) {
++ buf_unlock(sb, sector);
++ return;
++ }
++ num_entries++;
++
++ buf_unlock(sb, sector);
++
++ p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries);
++}
++
++INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
++{
++ INT32 ret, newentry = -1, num_old_entries, num_new_entries;
++ UINT32 sector_old, sector_new;
++ DOS_NAME_T dos_name;
++ DENTRY_T *epold, *epnew;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ epold = get_entry_in_dir(sb, p_dir, oldentry, &sector_old);
++ if (!epold)
++ return FFS_MEDIAERR;
++
++ buf_lock(sb, sector_old);
++
++ num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold);
++ if (num_old_entries < 0) {
++ buf_unlock(sb, sector_old);
++ return FFS_MEDIAERR;
++ }
++ num_old_entries++;
++
++ ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name);
++ if (ret) {
++ buf_unlock(sb, sector_old);
++ return ret;
++ }
++
++ if (num_old_entries < num_new_entries) {
++ newentry = find_empty_entry(inode, p_dir, num_new_entries);
++ if (newentry < 0) {
++ buf_unlock(sb, sector_old);
++ return FFS_FULL;
++ }
++
++ epnew = get_entry_in_dir(sb, p_dir, newentry, &sector_new);
++ if (!epnew) {
++ buf_unlock(sb, sector_old);
++ return FFS_MEDIAERR;
++ }
++
++ MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE);
++ if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) {
++ p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
++ fid->attr |= ATTR_ARCHIVE;
++ }
++ buf_modify(sb, sector_new);
++ buf_unlock(sb, sector_old);
++
++ if (p_fs->vol_type == EXFAT) {
++ epold = get_entry_in_dir(sb, p_dir, oldentry+1, &sector_old);
++ buf_lock(sb, sector_old);
++ epnew = get_entry_in_dir(sb, p_dir, newentry+1, &sector_new);
++
++ if (!epold || !epnew) {
++ buf_unlock(sb, sector_old);
++ return FFS_MEDIAERR;
++ }
++
++ MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE);
++ buf_modify(sb, sector_new);
++ buf_unlock(sb, sector_old);
++ }
++
++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries);
++ fid->entry = newentry;
++ } else {
++ if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) {
++ p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE);
++ fid->attr |= ATTR_ARCHIVE;
++ }
++ buf_modify(sb, sector_old);
++ buf_unlock(sb, sector_old);
++
++ ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries);
++ }
++
++ return FFS_SUCCESS;
++}
++
++INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid)
++{
++ INT32 ret, newentry, num_new_entries, num_old_entries;
++ UINT32 sector_mov, sector_new;
++ CHAIN_T clu;
++ DOS_NAME_T dos_name;
++ DENTRY_T *epmov, *epnew;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ epmov = get_entry_in_dir(sb, p_olddir, oldentry, &sector_mov);
++ if (!epmov)
++ return FFS_MEDIAERR;
++
++ if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR &&
++ p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir)
++ return FFS_INVALIDPATH;
++
++ buf_lock(sb, sector_mov);
++
++ num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov);
++ if (num_old_entries < 0) {
++ buf_unlock(sb, sector_mov);
++ return FFS_MEDIAERR;
++ }
++ num_old_entries++;
++
++ ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name);
++ if (ret) {
++ buf_unlock(sb, sector_mov);
++ return ret;
++ }
++
++ newentry = find_empty_entry(inode, p_newdir, num_new_entries);
++ if (newentry < 0) {
++ buf_unlock(sb, sector_mov);
++ return FFS_FULL;
++ }
++
++ epnew = get_entry_in_dir(sb, p_newdir, newentry, &sector_new);
++ if (!epnew) {
++ buf_unlock(sb, sector_mov);
++ return FFS_MEDIAERR;
++ }
++
++ MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE);
++ if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) {
++ p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE);
++ fid->attr |= ATTR_ARCHIVE;
++ }
++ buf_modify(sb, sector_new);
++ buf_unlock(sb, sector_mov);
++
++ if (p_fs->vol_type == EXFAT) {
++ epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, &sector_mov);
++ buf_lock(sb, sector_mov);
++ epnew = get_entry_in_dir(sb, p_newdir, newentry+1, &sector_new);
++ if (!epmov || !epnew) {
++ buf_unlock(sb, sector_mov);
++ return FFS_MEDIAERR;
++ }
++
++ MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE);
++ buf_modify(sb, sector_new);
++ buf_unlock(sb, sector_mov);
++ } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) {
++ clu.dir = p_fs->fs_func->get_entry_clu0(epnew);
++ clu.flags = 0x01;
++
++ epnew = get_entry_in_dir(sb, &clu, 1, &sector_new);
++ if (!epnew)
++ return FFS_MEDIAERR;
++
++ if (p_newdir->dir == p_fs->root_dir)
++ p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0));
++ else
++ p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir);
++ buf_modify(sb, sector_new);
++ }
++
++ ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name);
++ if (ret != FFS_SUCCESS)
++ return ret;
++
++ p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries);
++
++ fid->dir.dir = p_newdir->dir;
++ fid->dir.size = p_newdir->size;
++ fid->dir.flags = p_newdir->flags;
++
++ fid->entry = newentry;
++
++ return FFS_SUCCESS;
++}
++
++INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read)
++{
++ INT32 ret = FFS_MEDIAERR;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) {
++ PRINT("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec);
++ fs_error(sb);
++ return ret;
++ }
++
++ if (!p_fs->dev_ejected) {
++ ret = bdev_read(sb, sec, bh, 1, read);
++ if (ret != FFS_SUCCESS)
++ p_fs->dev_ejected = TRUE;
++ }
++
++ return ret;
++}
++
++INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync)
++{
++ INT32 ret = FFS_MEDIAERR;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) {
++ PRINT("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec);
++ fs_error(sb);
++ return ret;
++ }
++
++ if (bh == NULL) {
++ PRINT("[EXFAT] sector_write: bh is NULL!\n");
++ fs_error(sb);
++ return ret;
++ }
++
++ if (!p_fs->dev_ejected) {
++ ret = bdev_write(sb, sec, bh, 1, sync);
++ if (ret != FFS_SUCCESS)
++ p_fs->dev_ejected = TRUE;
++ }
++
++ return ret;
++}
++
++INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read)
++{
++ INT32 ret = FFS_MEDIAERR;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) {
++ PRINT("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs);
++ fs_error(sb);
++ return ret;
++ }
++
++ if (!p_fs->dev_ejected) {
++ ret = bdev_read(sb, sec, bh, num_secs, read);
++ if (ret != FFS_SUCCESS)
++ p_fs->dev_ejected = TRUE;
++ }
++
++ return ret;
++}
++
++INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync)
++{
++ INT32 ret = FFS_MEDIAERR;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) {
++ PRINT("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs);
++ fs_error(sb);
++ return ret;
++ }
++ if (bh == NULL) {
++ PRINT("[EXFAT] multi_sector_write: bh is NULL!\n");
++ fs_error(sb);
++ return ret;
++ }
++
++ if (!p_fs->dev_ejected) {
++ ret = bdev_write(sb, sec, bh, num_secs, sync);
++ if (ret != FFS_SUCCESS)
++ p_fs->dev_ejected = TRUE;
++ }
++
++ return ret;
++}
+diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h
+new file mode 100644
+index 00000000..a10f4894
+--- /dev/null
++++ b/drivers/staging/exfat/exfat.h
+@@ -0,0 +1,598 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_H
++#define _EXFAT_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_blkdev.h"
++#include "exfat_cache.h"
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat_cache.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#if EXFAT_CONFIG_KERNEL_DEBUG
++#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long)
++#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long)
++
++#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01
++#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02
++#endif
++
++#define MAX_VOLUME 4
++
++#define DENTRY_SIZE 32
++#define DENTRY_SIZE_BITS 5
++
++#define PBR_SIGNATURE 0xAA55
++#define EXT_SIGNATURE 0xAA550000
++#define VOL_LABEL "NO NAME "
++#define OEM_NAME "MSWIN4.1"
++#define STR_FAT12 "FAT12 "
++#define STR_FAT16 "FAT16 "
++#define STR_FAT32 "FAT32 "
++#define STR_EXFAT "EXFAT "
++#define VOL_CLEAN 0x0000
++#define VOL_DIRTY 0x0002
++
++#define FAT12_THRESHOLD 4087
++#define FAT16_THRESHOLD 65527
++#define FAT32_THRESHOLD 268435457
++#define EXFAT_THRESHOLD 268435457
++
++#define TYPE_UNUSED 0x0000
++#define TYPE_DELETED 0x0001
++#define TYPE_INVALID 0x0002
++#define TYPE_CRITICAL_PRI 0x0100
++#define TYPE_BITMAP 0x0101
++#define TYPE_UPCASE 0x0102
++#define TYPE_VOLUME 0x0103
++#define TYPE_DIR 0x0104
++#define TYPE_FILE 0x011F
++#define TYPE_SYMLINK 0x015F
++#define TYPE_CRITICAL_SEC 0x0200
++#define TYPE_STREAM 0x0201
++#define TYPE_EXTEND 0x0202
++#define TYPE_ACL 0x0203
++#define TYPE_BENIGN_PRI 0x0400
++#define TYPE_GUID 0x0401
++#define TYPE_PADDING 0x0402
++#define TYPE_ACLTAB 0x0403
++#define TYPE_BENIGN_SEC 0x0800
++#define TYPE_ALL 0x0FFF
++
++#define TM_CREATE 0
++#define TM_MODIFY 1
++#define TM_ACCESS 2
++
++#define CS_DIR_ENTRY 0
++#define CS_PBR_SECTOR 1
++#define CS_DEFAULT 2
++
++#define CLUSTER_16(x) ((UINT16)(x))
++#define CLUSTER_32(x) ((UINT32)(x))
++
++#define START_SECTOR(x) \
++ ( (((x)-2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector )
++
++#define IS_LAST_SECTOR_IN_CLUSTER(sec) \
++ ( (((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) -1)) == 0)
++
++#define GET_CLUSTER_FROM_SECTOR(sec) \
++ ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) +2)
++
++#define GET16(p_src) \
++ ( ((UINT16)(p_src)[0]) | (((UINT16)(p_src)[1]) << 8) )
++#define GET32(p_src) \
++ ( ((UINT32)(p_src)[0]) | (((UINT32)(p_src)[1]) << 8) | \
++ (((UINT32)(p_src)[2]) << 16) | (((UINT32)(p_src)[3]) << 24) )
++#define GET64(p_src) \
++ ( ((UINT64)(p_src)[0]) | (((UINT64)(p_src)[1]) << 8) | \
++ (((UINT64)(p_src)[2]) << 16) | (((UINT64)(p_src)[3]) << 24) | \
++ (((UINT64)(p_src)[4]) << 32) | (((UINT64)(p_src)[5]) << 40) | \
++ (((UINT64)(p_src)[6]) << 48) | (((UINT64)(p_src)[7]) << 56) )
++
++
++#define SET16(p_dst,src) \
++ do { \
++ (p_dst)[0]=(UINT8)(src); \
++ (p_dst)[1]=(UINT8)(((UINT16)(src)) >> 8); \
++ } while (0)
++#define SET32(p_dst,src) \
++ do { \
++ (p_dst)[0]=(UINT8)(src); \
++ (p_dst)[1]=(UINT8)(((UINT32)(src)) >> 8); \
++ (p_dst)[2]=(UINT8)(((UINT32)(src)) >> 16); \
++ (p_dst)[3]=(UINT8)(((UINT32)(src)) >> 24); \
++ } while (0)
++#define SET64(p_dst,src) \
++ do { \
++ (p_dst)[0]=(UINT8)(src); \
++ (p_dst)[1]=(UINT8)(((UINT64)(src)) >> 8); \
++ (p_dst)[2]=(UINT8)(((UINT64)(src)) >> 16); \
++ (p_dst)[3]=(UINT8)(((UINT64)(src)) >> 24); \
++ (p_dst)[4]=(UINT8)(((UINT64)(src)) >> 32); \
++ (p_dst)[5]=(UINT8)(((UINT64)(src)) >> 40); \
++ (p_dst)[6]=(UINT8)(((UINT64)(src)) >> 48); \
++ (p_dst)[7]=(UINT8)(((UINT64)(src)) >> 56); \
++ } while (0)
++
++#if (FFS_CONFIG_LITTLE_ENDIAN == 1)
++#define GET16_A(p_src) (*((UINT16 *)(p_src)))
++#define GET32_A(p_src) (*((UINT32 *)(p_src)))
++#define GET64_A(p_src) (*((UINT64 *)(p_src)))
++#define SET16_A(p_dst,src) *((UINT16 *)(p_dst)) = (UINT16)(src)
++#define SET32_A(p_dst,src) *((UINT32 *)(p_dst)) = (UINT32)(src)
++#define SET64_A(p_dst,src) *((UINT64 *)(p_dst)) = (UINT64)(src)
++#else
++#define GET16_A(p_src) GET16(p_src)
++#define GET32_A(p_src) GET32(p_src)
++#define GET64_A(p_src) GET64(p_src)
++#define SET16_A(p_dst,src) SET16(p_dst, src)
++#define SET32_A(p_dst,src) SET32(p_dst, src)
++#define SET64_A(p_dst,src) SET64(p_dst, src)
++#endif
++
++#define HIGH_INDEX_BIT (8)
++#define HIGH_INDEX_MASK (0xFF00)
++#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT)
++#define UTBL_ROW_COUNT (1<<LOW_INDEX_BIT)
++#define UTBL_COL_COUNT (1<<HIGH_INDEX_BIT)
++
++ static inline UINT16 get_col_index(UINT16 i)
++ {
++ return i >> LOW_INDEX_BIT;
++ }
++ static inline UINT16 get_row_index(UINT16 i)
++ {
++ return i & ~HIGH_INDEX_MASK;
++ }
++
++ typedef struct {
++ UINT8 jmp_boot[3];
++ UINT8 oem_name[8];
++ UINT8 bpb[109];
++ UINT8 boot_code[390];
++ UINT8 signature[2];
++ } PBR_SECTOR_T;
++
++ typedef struct {
++ UINT8 sector_size[2];
++ UINT8 sectors_per_clu;
++ UINT8 num_reserved[2];
++ UINT8 num_fats;
++ UINT8 num_root_entries[2];
++ UINT8 num_sectors[2];
++ UINT8 media_type;
++ UINT8 num_fat_sectors[2];
++ UINT8 sectors_in_track[2];
++ UINT8 num_heads[2];
++ UINT8 num_hid_sectors[4];
++ UINT8 num_huge_sectors[4];
++
++ UINT8 phy_drv_no;
++ UINT8 reserved;
++ UINT8 ext_signature;
++ UINT8 vol_serial[4];
++ UINT8 vol_label[11];
++ UINT8 vol_type[8];
++ } BPB16_T;
++
++ typedef struct {
++ UINT8 sector_size[2];
++ UINT8 sectors_per_clu;
++ UINT8 num_reserved[2];
++ UINT8 num_fats;
++ UINT8 num_root_entries[2];
++ UINT8 num_sectors[2];
++ UINT8 media_type;
++ UINT8 num_fat_sectors[2];
++ UINT8 sectors_in_track[2];
++ UINT8 num_heads[2];
++ UINT8 num_hid_sectors[4];
++ UINT8 num_huge_sectors[4];
++ UINT8 num_fat32_sectors[4];
++ UINT8 ext_flags[2];
++ UINT8 fs_version[2];
++ UINT8 root_cluster[4];
++ UINT8 fsinfo_sector[2];
++ UINT8 backup_sector[2];
++ UINT8 reserved[12];
++
++ UINT8 phy_drv_no;
++ UINT8 ext_reserved;
++ UINT8 ext_signature;
++ UINT8 vol_serial[4];
++ UINT8 vol_label[11];
++ UINT8 vol_type[8];
++ } BPB32_T;
++
++ typedef struct {
++ UINT8 reserved1[53];
++ UINT8 vol_offset[8];
++ UINT8 vol_length[8];
++ UINT8 fat_offset[4];
++ UINT8 fat_length[4];
++ UINT8 clu_offset[4];
++ UINT8 clu_count[4];
++ UINT8 root_cluster[4];
++ UINT8 vol_serial[4];
++ UINT8 fs_version[2];
++ UINT8 vol_flags[2];
++ UINT8 sector_size_bits;
++ UINT8 sectors_per_clu_bits;
++ UINT8 num_fats;
++ UINT8 phy_drv_no;
++ UINT8 perc_in_use;
++ UINT8 reserved2[7];
++ } BPBEX_T;
++
++ typedef struct {
++ UINT8 signature1[4];
++ UINT8 reserved1[480];
++ UINT8 signature2[4];
++ UINT8 free_cluster[4];
++ UINT8 next_cluster[4];
++ UINT8 reserved2[14];
++ UINT8 signature3[2];
++ } FSI_SECTOR_T;
++
++ typedef struct {
++ UINT8 dummy[32];
++ } DENTRY_T;
++
++ typedef struct {
++ UINT8 name[DOS_NAME_LENGTH];
++ UINT8 attr;
++ UINT8 lcase;
++ UINT8 create_time_ms;
++ UINT8 create_time[2];
++ UINT8 create_date[2];
++ UINT8 access_date[2];
++ UINT8 start_clu_hi[2];
++ UINT8 modify_time[2];
++ UINT8 modify_date[2];
++ UINT8 start_clu_lo[2];
++ UINT8 size[4];
++ } DOS_DENTRY_T;
++
++ typedef struct {
++ UINT8 order;
++ UINT8 unicode_0_4[10];
++ UINT8 attr;
++ UINT8 sysid;
++ UINT8 checksum;
++ UINT8 unicode_5_10[12];
++ UINT8 start_clu[2];
++ UINT8 unicode_11_12[4];
++ } EXT_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 num_ext;
++ UINT8 checksum[2];
++ UINT8 attr[2];
++ UINT8 reserved1[2];
++ UINT8 create_time[2];
++ UINT8 create_date[2];
++ UINT8 modify_time[2];
++ UINT8 modify_date[2];
++ UINT8 access_time[2];
++ UINT8 access_date[2];
++ UINT8 create_time_ms;
++ UINT8 modify_time_ms;
++ UINT8 access_time_ms;
++ UINT8 reserved2[9];
++ } FILE_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 flags;
++ UINT8 reserved1;
++ UINT8 name_len;
++ UINT8 name_hash[2];
++ UINT8 reserved2[2];
++ UINT8 valid_size[8];
++ UINT8 reserved3[4];
++ UINT8 start_clu[4];
++ UINT8 size[8];
++ } STRM_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 flags;
++ UINT8 unicode_0_14[30];
++ } NAME_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 flags;
++ UINT8 reserved[18];
++ UINT8 start_clu[4];
++ UINT8 size[8];
++ } BMAP_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 reserved1[3];
++ UINT8 checksum[4];
++ UINT8 reserved2[12];
++ UINT8 start_clu[4];
++ UINT8 size[8];
++ } CASE_DENTRY_T;
++
++ typedef struct {
++ UINT8 type;
++ UINT8 label_len;
++ UINT8 unicode_0_10[22];
++ UINT8 reserved[8];
++ } VOLM_DENTRY_T;
++
++ typedef struct {
++ UINT32 dir;
++ INT32 entry;
++ CHAIN_T clu;
++ } UENTRY_T;
++
++ typedef struct __FS_STRUCT_T {
++ UINT32 mounted;
++ struct super_block *sb;
++ struct semaphore v_sem;
++ } FS_STRUCT_T;
++
++ typedef struct {
++ INT32 (*alloc_cluster)(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain);
++ void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse);
++ INT32 (*count_used_clusters)(struct super_block *sb);
++
++ INT32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type,
++ UINT32 start_clu, UINT64 size);
++ INT32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries,
++ UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
++ INT32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type);
++ void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 offset, INT32 num_entries);
++ void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname);
++ INT32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry);
++ INT32 (*calc_num_entries)(UNI_NAME_T *p_uniname);
++
++ UINT32 (*get_entry_type)(DENTRY_T *p_entry);
++ void (*set_entry_type)(DENTRY_T *p_entry, UINT32 type);
++ UINT32 (*get_entry_attr)(DENTRY_T *p_entry);
++ void (*set_entry_attr)(DENTRY_T *p_entry, UINT32 attr);
++ UINT8 (*get_entry_flag)(DENTRY_T *p_entry);
++ void (*set_entry_flag)(DENTRY_T *p_entry, UINT8 flag);
++ UINT32 (*get_entry_clu0)(DENTRY_T *p_entry);
++ void (*set_entry_clu0)(DENTRY_T *p_entry, UINT32 clu0);
++ UINT64 (*get_entry_size)(DENTRY_T *p_entry);
++ void (*set_entry_size)(DENTRY_T *p_entry, UINT64 size);
++ void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ } FS_FUNC_T;
++
++ typedef struct __FS_INFO_T {
++ UINT32 drv;
++ UINT32 vol_type;
++ UINT32 vol_id;
++
++ UINT32 num_sectors;
++ UINT32 num_clusters;
++ UINT32 cluster_size;
++ UINT32 cluster_size_bits;
++ UINT32 sectors_per_clu;
++ UINT32 sectors_per_clu_bits;
++
++ UINT32 PBR_sector;
++ UINT32 FAT1_start_sector;
++ UINT32 FAT2_start_sector;
++ UINT32 root_start_sector;
++ UINT32 data_start_sector;
++ UINT32 num_FAT_sectors;
++
++ UINT32 root_dir;
++ UINT32 dentries_in_root;
++ UINT32 dentries_per_clu;
++
++ UINT32 vol_flag;
++ struct buffer_head *pbr_bh;
++
++ UINT32 map_clu;
++ UINT32 map_sectors;
++ struct buffer_head **vol_amap;
++
++ UINT16 **vol_utbl;
++
++ UINT32 clu_srch_ptr;
++ UINT32 used_clusters;
++ UENTRY_T hint_uentry;
++
++ UINT32 dev_ejected;
++
++ FS_FUNC_T *fs_func;
++
++ BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE];
++ BUF_CACHE_T FAT_cache_lru_list;
++ BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE];
++
++ BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE];
++ BUF_CACHE_T buf_cache_lru_list;
++ BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE];
++ } FS_INFO_T;
++
++#define ES_2_ENTRIES 2
++#define ES_3_ENTRIES 3
++#define ES_ALL_ENTRIES 0
++
++ typedef struct {
++ UINT32 sector;
++ INT32 offset;
++ INT32 alloc_flag;
++ UINT32 num_entries;
++
++ void *__buf;
++ } ENTRY_SET_CACHE_T;
++
++ INT32 ffsInit(void);
++ INT32 ffsShutdown(void);
++
++ INT32 ffsMountVol(struct super_block *sb, INT32 drv);
++ INT32 ffsUmountVol(struct super_block *sb);
++ INT32 ffsCheckVol(struct super_block *sb);
++ INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info);
++ INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync);
++
++ INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid);
++ INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid);
++ INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount);
++ INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount);
++ INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size);
++ INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry);
++ INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid);
++ INT32 ffsSetAttr(struct inode *inode, UINT32 attr);
++ INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info);
++ INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info);
++ INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu);
++
++ INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid);
++ INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent);
++ INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid);
++
++ INT32 fs_init(void);
++ INT32 fs_shutdown(void);
++ void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag);
++ void fs_sync(struct super_block *sb, INT32 do_sync);
++ void fs_error(struct super_block *sb);
++
++ INT32 clear_cluster(struct super_block *sb, UINT32 clu);
++ INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain);
++ INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain);
++ void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse);
++ void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse);
++ UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain);
++ INT32 count_num_clusters(struct super_block *sb, CHAIN_T *dir);
++ INT32 fat_count_used_clusters(struct super_block *sb);
++ INT32 exfat_count_used_clusters(struct super_block *sb);
++ void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len);
++
++ INT32 load_alloc_bitmap(struct super_block *sb);
++ void free_alloc_bitmap(struct super_block *sb);
++ INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu);
++ INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu);
++ UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu);
++ void sync_alloc_bitmap(struct super_block *sb);
++
++ INT32 load_upcase_table(struct super_block *sb);
++ void free_upcase_table(struct super_block *sb);
++
++ UINT32 fat_get_entry_type(DENTRY_T *p_entry);
++ UINT32 exfat_get_entry_type(DENTRY_T *p_entry);
++ void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type);
++ void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type);
++ UINT32 fat_get_entry_attr(DENTRY_T *p_entry);
++ UINT32 exfat_get_entry_attr(DENTRY_T *p_entry);
++ void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr);
++ void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr);
++ UINT8 fat_get_entry_flag(DENTRY_T *p_entry);
++ UINT8 exfat_get_entry_flag(DENTRY_T *p_entry);
++ void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag);
++ void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag);
++ UINT32 fat_get_entry_clu0(DENTRY_T *p_entry);
++ UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry);
++ void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu);
++ void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu);
++ UINT64 fat_get_entry_size(DENTRY_T *p_entry);
++ UINT64 exfat_get_entry_size(DENTRY_T *p_entry);
++ void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size);
++ void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size);
++ void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode);
++ INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size);
++ INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size);
++ INT32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
++ INT32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
++ void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu);
++ void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname);
++ void init_file_entry(FILE_DENTRY_T *ep, UINT32 type);
++ void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size);
++ void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname);
++ void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries);
++ void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries);
++
++ INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset);
++ DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset);
++ DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector);
++ ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep);
++ void release_entry_set (ENTRY_SET_CACHE_T *es);
++ INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es);
++ INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count);
++ INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries);
++ INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries);
++ INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type);
++ INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type);
++ INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry);
++ INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry);
++ INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type);
++ void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry);
++ void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es);
++ BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir);
++
++ INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname);
++ void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode);
++ void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname);
++ void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname);
++ INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order);
++ INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order);
++ INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname);
++ void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count);
++ INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname);
++ INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname);
++ UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum);
++ UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type);
++ UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type);
++
++ INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname);
++ INT32 resolve_name(UINT8 *name, UINT8 **arg);
++
++ INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
++ INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
++ INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr);
++ INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
++ INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid);
++ void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry);
++ INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
++ INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid);
++
++ INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read);
++ INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync);
++ INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read);
++ INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_api.c b/drivers/staging/exfat/exfat_api.c
+new file mode 100644
+index 00000000..84a0b434
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_api.c
+@@ -0,0 +1,457 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include "exfat_version.h"
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_part.h"
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat_super.h"
++#include "exfat.h"
++
++extern FS_STRUCT_T fs_struct[];
++
++extern struct semaphore z_sem;
++
++INT32 FsInit(void)
++{
++ INT32 i;
++
++ for (i = 0; i < MAX_DRIVE; i++) {
++ fs_struct[i].mounted = FALSE;
++ fs_struct[i].sb = NULL;
++ sm_init(&(fs_struct[i].v_sem));
++ }
++
++ return(ffsInit());
++}
++
++INT32 FsShutdown(void)
++{
++ INT32 i;
++
++ for (i = 0; i < MAX_DRIVE; i++) {
++ if (!fs_struct[i].mounted) continue;
++
++ ffsUmountVol(fs_struct[i].sb);
++ }
++
++ return(ffsShutdown());
++}
++
++INT32 FsMountVol(struct super_block *sb)
++{
++ INT32 err, drv;
++
++ sm_P(&z_sem);
++
++ for (drv = 0; drv < MAX_DRIVE; drv++) {
++ if (!fs_struct[drv].mounted) break;
++ }
++
++ if (drv >= MAX_DRIVE) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[drv].v_sem));
++
++ err = buf_init(sb);
++ if (!err) {
++ err = ffsMountVol(sb, drv);
++ }
++
++ sm_V(&(fs_struct[drv].v_sem));
++
++ if (!err) {
++ fs_struct[drv].mounted = TRUE;
++ fs_struct[drv].sb = sb;
++ } else {
++ buf_shutdown(sb);
++ }
++
++ sm_V(&z_sem);
++
++ return(err);
++}
++
++INT32 FsUmountVol(struct super_block *sb)
++{
++ INT32 err;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&z_sem);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsUmountVol(sb);
++ buf_shutdown(sb);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ fs_struct[p_fs->drv].mounted = FALSE;
++ fs_struct[p_fs->drv].sb = NULL;
++
++ sm_V(&z_sem);
++
++ return(err);
++}
++
++INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info)
++{
++ INT32 err;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (info == NULL) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsGetVolInfo(sb, info);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsSyncVol(struct super_block *sb, INT32 do_sync)
++{
++ INT32 err;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsSyncVol(sb, do_sync);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0))
++ return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsLookupFile(inode, path, fid);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0))
++ return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsCreateFile(inode, path, mode, fid);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (fid == NULL) return(FFS_INVALIDFID);
++
++ if (buffer == NULL) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsReadFile(inode, fid, buffer, count, rcount);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (fid == NULL) return(FFS_INVALIDFID);
++
++ if (buffer == NULL) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsWriteFile(inode, fid, buffer, count, wcount);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ PRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size);
++
++ err = ffsTruncateFile(inode, old_size, new_size);
++
++ PRINTK("FsTruncateFile exitted (%d)\n", err);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry)
++{
++ INT32 err;
++ struct super_block *sb = old_parent_inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (fid == NULL) return(FFS_INVALIDFID);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (fid == NULL) return(FFS_INVALIDFID);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsRemoveFile(inode, fid);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsSetAttr(struct inode *inode, UINT32 attr)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsSetAttr(inode, attr);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsGetStat(inode, info);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ PRINTK("FsWriteStat entered (inode %p info %p\n", inode, info);
++
++ err = ffsSetStat(inode, info);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ PRINTK("FsWriteStat exited (%d)\n", err);
++
++ return(err);
++}
++
++INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (clu == NULL) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsMapCluster(inode, clu_offset, clu);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if ((fid == NULL) || (path == NULL) || (STRLEN(path) == 0))
++ return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsCreateDir(inode, path, fid);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (dir_entry == NULL) return(FFS_ERROR);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsReadDir(inode, dir_entry);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid)
++{
++ INT32 err;
++ struct super_block *sb = inode->i_sb;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (fid == NULL) return(FFS_INVALIDFID);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ err = ffsRemoveDir(inode, fid);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return(err);
++}
++
++EXPORT_SYMBOL(FsMountVol);
++EXPORT_SYMBOL(FsUmountVol);
++EXPORT_SYMBOL(FsGetVolInfo);
++EXPORT_SYMBOL(FsSyncVol);
++EXPORT_SYMBOL(FsLookupFile);
++EXPORT_SYMBOL(FsCreateFile);
++EXPORT_SYMBOL(FsReadFile);
++EXPORT_SYMBOL(FsWriteFile);
++EXPORT_SYMBOL(FsTruncateFile);
++EXPORT_SYMBOL(FsMoveFile);
++EXPORT_SYMBOL(FsRemoveFile);
++EXPORT_SYMBOL(FsSetAttr);
++EXPORT_SYMBOL(FsReadStat);
++EXPORT_SYMBOL(FsWriteStat);
++EXPORT_SYMBOL(FsMapCluster);
++EXPORT_SYMBOL(FsCreateDir);
++EXPORT_SYMBOL(FsReadDir);
++EXPORT_SYMBOL(FsRemoveDir);
++
++#if EXFAT_CONFIG_KERNEL_DEBUG
++INT32 FsReleaseCache(struct super_block *sb)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&(fs_struct[p_fs->drv].v_sem));
++
++ FAT_release_all(sb);
++ buf_release_all(sb);
++
++ sm_V(&(fs_struct[p_fs->drv].v_sem));
++
++ return 0;
++}
++
++EXPORT_SYMBOL(FsReleaseCache);
++#endif
++
++static int __init init_exfat_core(void)
++{
++ int err;
++
++ printk(KERN_INFO "exFAT: Core Version %s\n", EXFAT_VERSION);
++
++ err = FsInit();
++ if (err) {
++ if (err == FFS_MEMORYERR)
++ return -ENOMEM;
++ else
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static void __exit exit_exfat_core(void)
++{
++ FsShutdown();
++}
++
++module_init(init_exfat_core);
++module_exit(exit_exfat_core);
++
++MODULE_LICENSE("GPL");
+diff --git a/drivers/staging/exfat/exfat_api.h b/drivers/staging/exfat/exfat_api.h
+new file mode 100644
+index 00000000..8de658b1
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_api.h
+@@ -0,0 +1,165 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_API_H
++#define _EXFAT_API_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define EXFAT_SUPER_MAGIC (0x2011BAB0L)
++#define EXFAT_ROOT_INO 1
++
++#define FAT12 0x01
++#define FAT16 0x0E
++#define FAT32 0x0C
++#define EXFAT 0x07
++
++#define MAX_CHARSET_SIZE 3
++#define MAX_PATH_DEPTH 15
++#define MAX_NAME_LENGTH 256
++#define MAX_PATH_LENGTH 260
++#define DOS_NAME_LENGTH 11
++#define DOS_PATH_LENGTH 80
++
++#define ATTR_NORMAL 0x0000
++#define ATTR_READONLY 0x0001
++#define ATTR_HIDDEN 0x0002
++#define ATTR_SYSTEM 0x0004
++#define ATTR_VOLUME 0x0008
++#define ATTR_SUBDIR 0x0010
++#define ATTR_ARCHIVE 0x0020
++#define ATTR_SYMLINK 0x0040
++#define ATTR_EXTEND 0x000F
++#define ATTR_RWMASK 0x007E
++
++#define FM_REGULAR 0x00
++#define FM_SYMLINK 0x40
++
++#define FFS_SUCCESS 0
++#define FFS_MEDIAERR 1
++#define FFS_FORMATERR 2
++#define FFS_MOUNTED 3
++#define FFS_NOTMOUNTED 4
++#define FFS_ALIGNMENTERR 5
++#define FFS_SEMAPHOREERR 6
++#define FFS_INVALIDPATH 7
++#define FFS_INVALIDFID 8
++#define FFS_NOTFOUND 9
++#define FFS_FILEEXIST 10
++#define FFS_PERMISSIONERR 11
++#define FFS_NOTOPENED 12
++#define FFS_MAXOPENED 13
++#define FFS_FULL 14
++#define FFS_EOF 15
++#define FFS_DIRBUSY 16
++#define FFS_MEMORYERR 17
++#define FFS_NAMETOOLONG 18
++#define FFS_ERROR 19
++
++ typedef struct {
++ UINT16 Year;
++ UINT16 Month;
++ UINT16 Day;
++ UINT16 Hour;
++ UINT16 Minute;
++ UINT16 Second;
++ UINT16 MilliSecond;
++ } DATE_TIME_T;
++
++ typedef struct {
++ UINT32 Offset;
++ UINT32 Size;
++ } PART_INFO_T;
++
++ typedef struct {
++ UINT32 SecSize;
++ UINT32 DevSize;
++ } DEV_INFO_T;
++
++ typedef struct {
++ UINT32 FatType;
++ UINT32 ClusterSize;
++ UINT32 NumClusters;
++ UINT32 FreeClusters;
++ UINT32 UsedClusters;
++ } VOL_INFO_T;
++
++ typedef struct {
++ UINT32 dir;
++ INT32 size;
++ UINT8 flags;
++ } CHAIN_T;
++
++ typedef struct {
++ CHAIN_T dir;
++ INT32 entry;
++ UINT32 type;
++ UINT32 attr;
++ UINT32 start_clu;
++ UINT64 size;
++ UINT8 flags;
++ INT64 rwoffset;
++ INT32 hint_last_off;
++ UINT32 hint_last_clu;
++ } FILE_ID_T;
++
++ typedef struct {
++ INT8 Name[MAX_NAME_LENGTH *MAX_CHARSET_SIZE];
++ INT8 ShortName[DOS_NAME_LENGTH + 2];
++ UINT32 Attr;
++ UINT64 Size;
++ UINT32 NumSubdirs;
++ DATE_TIME_T CreateTimestamp;
++ DATE_TIME_T ModifyTimestamp;
++ DATE_TIME_T AccessTimestamp;
++ } DIR_ENTRY_T;
++
++ INT32 FsInit(void);
++ INT32 FsShutdown(void);
++
++ INT32 FsMountVol(struct super_block *sb);
++ INT32 FsUmountVol(struct super_block *sb);
++ INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info);
++ INT32 FsSyncVol(struct super_block *sb, INT32 do_sync);
++
++ INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid);
++ INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid);
++ INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount);
++ INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount);
++ INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size);
++ INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry);
++ INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid);
++ INT32 FsSetAttr(struct inode *inode, UINT32 attr);
++ INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info);
++ INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info);
++ INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu);
++
++ INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid);
++ INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry);
++ INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid);
++
++ INT32 FsReleaseCache(struct super_block *sb);
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/drivers/staging/exfat/exfat_blkdev.c b/drivers/staging/exfat/exfat_blkdev.c
+new file mode 100644
+index 00000000..c5bf7a7a
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_blkdev.c
+@@ -0,0 +1,156 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include <linux/blkdev.h>
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_blkdev.h"
++#include "exfat_data.h"
++#include "exfat_api.h"
++#include "exfat_super.h"
++
++INT32 bdev_init(void)
++{
++ return(FFS_SUCCESS);
++}
++
++INT32 bdev_shutdown(void)
++{
++ return(FFS_SUCCESS);
++}
++
++INT32 bdev_open(struct super_block *sb)
++{
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_bd->opened) return(FFS_SUCCESS);
++
++ p_bd->sector_size = bdev_logical_block_size(sb->s_bdev);
++ p_bd->sector_size_bits = my_log2(p_bd->sector_size);
++ p_bd->sector_size_mask = p_bd->sector_size - 1;
++ p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits;
++
++ p_bd->opened = TRUE;
++
++ return(FFS_SUCCESS);
++}
++
++INT32 bdev_close(struct super_block *sb)
++{
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (!p_bd->opened) return(FFS_SUCCESS);
++
++ p_bd->opened = FALSE;
++ return(FFS_SUCCESS);
++}
++
++INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read)
++{
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ long flags = sbi->debug_flags;
++
++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR);
++#endif
++
++ if (!p_bd->opened) return(FFS_MEDIAERR);
++
++ if (*bh) __brelse(*bh);
++
++ if (read)
++ *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits);
++ else
++ *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits);
++
++ if (*bh) return(FFS_SUCCESS);
++
++ WARN(!p_fs->dev_ejected,
++ "[EXFAT] No bh, device seems wrong or to be ejected.\n");
++
++ return(FFS_MEDIAERR);
++}
++
++INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync)
++{
++ INT32 count;
++ struct buffer_head *bh2;
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ long flags = sbi->debug_flags;
++
++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR);
++#endif
++
++ if (!p_bd->opened) return(FFS_MEDIAERR);
++
++ if (secno == bh->b_blocknr) {
++ lock_buffer(bh);
++ set_buffer_uptodate(bh);
++ mark_buffer_dirty(bh);
++ unlock_buffer(bh);
++ if (sync && (sync_dirty_buffer(bh) != 0))
++ return (FFS_MEDIAERR);
++ } else {
++ count = num_secs << p_bd->sector_size_bits;
++
++ bh2 = __getblk(sb->s_bdev, secno, count);
++
++ if (bh2 == NULL)
++ goto no_bh;
++
++ lock_buffer(bh2);
++ MEMCPY(bh2->b_data, bh->b_data, count);
++ set_buffer_uptodate(bh2);
++ mark_buffer_dirty(bh2);
++ unlock_buffer(bh2);
++ if (sync && (sync_dirty_buffer(bh2) != 0)) {
++ __brelse(bh2);
++ goto no_bh;
++ }
++ __brelse(bh2);
++ }
++
++ return(FFS_SUCCESS);
++
++no_bh:
++ WARN(!p_fs->dev_ejected,
++ "[EXFAT] No bh, device seems wrong or to be ejected.\n");
++
++ return (FFS_MEDIAERR);
++}
++
++INT32 bdev_sync(struct super_block *sb)
++{
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ long flags = sbi->debug_flags;
++
++ if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR);
++#endif
++
++ if (!p_bd->opened) return(FFS_MEDIAERR);
++
++ return sync_blockdev(sb->s_bdev);
++}
+diff --git a/drivers/staging/exfat/exfat_blkdev.h b/drivers/staging/exfat/exfat_blkdev.h
+new file mode 100644
+index 00000000..7710dd63
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_blkdev.h
+@@ -0,0 +1,46 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_BLKDEV_H
++#define _EXFAT_BLKDEV_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++ typedef struct __BD_INFO_T {
++ INT32 sector_size;
++ INT32 sector_size_bits;
++ INT32 sector_size_mask;
++ INT32 num_sectors;
++ BOOL opened;
++ } BD_INFO_T;
++
++ INT32 bdev_init(void);
++ INT32 bdev_shutdown(void);
++ INT32 bdev_open(struct super_block *sb);
++ INT32 bdev_close(struct super_block *sb);
++ INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read);
++ INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync);
++ INT32 bdev_sync(struct super_block *sb);
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c
+new file mode 100644
+index 00000000..008629fb
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_cache.c
+@@ -0,0 +1,740 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++
++#include "exfat_cache.h"
++#include "exfat_super.h"
++#include "exfat.h"
++
++extern FS_STRUCT_T fs_struct[];
++
++#define sm_P(s)
++#define sm_V(s)
++
++static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content);
++static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content);
++
++static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec);
++static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec);
++static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp);
++static void FAT_cache_remove_hash(BUF_CACHE_T *bp);
++
++static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec);
++
++static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec);
++static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec);
++static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp);
++static void buf_cache_remove_hash(BUF_CACHE_T *bp);
++
++static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list);
++static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list);
++static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list);
++static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list);
++
++INT32 buf_init(struct super_block *sb)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ INT32 i;
++
++ p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list;
++
++ for (i = 0; i < FAT_CACHE_SIZE; i++) {
++ p_fs->FAT_cache_array[i].drv = -1;
++ p_fs->FAT_cache_array[i].sec = ~0;
++ p_fs->FAT_cache_array[i].flag = 0;
++ p_fs->FAT_cache_array[i].buf_bh = NULL;
++ p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL;
++ push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list);
++ }
++
++ p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list;
++
++ for (i = 0; i < BUF_CACHE_SIZE; i++) {
++ p_fs->buf_cache_array[i].drv = -1;
++ p_fs->buf_cache_array[i].sec = ~0;
++ p_fs->buf_cache_array[i].flag = 0;
++ p_fs->buf_cache_array[i].buf_bh = NULL;
++ p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL;
++ push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list);
++ }
++
++ for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) {
++ p_fs->FAT_cache_hash_list[i].drv = -1;
++ p_fs->FAT_cache_hash_list[i].sec = ~0;
++ p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]);
++ }
++
++ for (i = 0; i < FAT_CACHE_SIZE; i++) {
++ FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i]));
++ }
++
++ for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) {
++ p_fs->buf_cache_hash_list[i].drv = -1;
++ p_fs->buf_cache_hash_list[i].sec = ~0;
++ p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]);
++ }
++
++ for (i = 0; i < BUF_CACHE_SIZE; i++) {
++ buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i]));
++ }
++
++ return(FFS_SUCCESS);
++}
++
++INT32 buf_shutdown(struct super_block *sb)
++{
++ return(FFS_SUCCESS);
++}
++
++INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content)
++{
++ INT32 ret;
++
++ sm_P(&f_sem);
++
++ ret = __FAT_read(sb, loc, content);
++
++ sm_V(&f_sem);
++
++ return(ret);
++}
++
++INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content)
++{
++ INT32 ret;
++
++ sm_P(&f_sem);
++
++ ret = __FAT_write(sb, loc, content);
++
++ sm_V(&f_sem);
++
++ return(ret);
++}
++
++static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content)
++{
++ INT32 off;
++ UINT32 sec, _content;
++ UINT8 *fat_sector, *fat_entry;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_fs->vol_type == FAT12) {
++ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits);
++ off = (loc + (loc >> 1)) & p_bd->sector_size_mask;
++
++ if (off == (p_bd->sector_size-1)) {
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ _content = (UINT32) fat_sector[off];
++
++ fat_sector = FAT_getblk(sb, ++sec);
++ if (!fat_sector)
++ return -1;
++
++ _content |= (UINT32) fat_sector[0] << 8;
++ } else {
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++ _content = GET16(fat_entry);
++ }
++
++ if (loc & 1) _content >>= 4;
++
++ _content &= 0x00000FFF;
++
++ if (_content >= CLUSTER_16(0x0FF8)) {
++ *content = CLUSTER_32(~0);
++ return 0;
++ } else {
++ *content = CLUSTER_32(_content);
++ return 0;
++ }
++ } else if (p_fs->vol_type == FAT16) {
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1));
++ off = (loc << 1) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if
++ (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++
++ _content = GET16_A(fat_entry);
++
++ _content &= 0x0000FFFF;
++
++ if (_content >= CLUSTER_16(0xFFF8)) {
++ *content = CLUSTER_32(~0);
++ return 0;
++ } else {
++ *content = CLUSTER_32(_content);
++ return 0;
++ }
++ } else if (p_fs->vol_type == FAT32) {
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2));
++ off = (loc << 2) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++
++ _content = GET32_A(fat_entry);
++
++ _content &= 0x0FFFFFFF;
++
++ if (_content >= CLUSTER_32(0x0FFFFFF8)) {
++ *content = CLUSTER_32(~0);
++ return 0;
++ } else {
++ *content = CLUSTER_32(_content);
++ return 0;
++ }
++ } else {
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2));
++ off = (loc << 2) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++ _content = GET32_A(fat_entry);
++
++ if (_content >= CLUSTER_32(0xFFFFFFF8)) {
++ *content = CLUSTER_32(~0);
++ return 0;
++ } else {
++ *content = CLUSTER_32(_content);
++ return 0;
++ }
++ }
++
++ *content = CLUSTER_32(~0);
++ return 0;
++}
++
++static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content)
++{
++ INT32 off;
++ UINT32 sec;
++ UINT8 *fat_sector, *fat_entry;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++
++ if (p_fs->vol_type == FAT12) {
++
++ content &= 0x00000FFF;
++
++ sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits);
++ off = (loc + (loc >> 1)) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ if (loc & 1) {
++
++ content <<= 4;
++
++ if (off == (p_bd->sector_size-1)) {
++ fat_sector[off] = (UINT8)(content | (fat_sector[off] & 0x0F));
++ FAT_modify(sb, sec);
++
++ fat_sector = FAT_getblk(sb, ++sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_sector[0] = (UINT8)(content >> 8);
++ } else {
++ fat_entry = &(fat_sector[off]);
++ content |= GET16(fat_entry) & 0x000F;
++
++ SET16(fat_entry, content);
++ }
++ } else {
++ fat_sector[off] = (UINT8)(content);
++
++ if (off == (p_bd->sector_size-1)) {
++ fat_sector[off] = (UINT8)(content);
++ FAT_modify(sb, sec);
++
++ fat_sector = FAT_getblk(sb, ++sec);
++ fat_sector[0] = (UINT8)((fat_sector[0] & 0xF0) | (content >> 8));
++ } else {
++ fat_entry = &(fat_sector[off]);
++ content |= GET16(fat_entry) & 0xF000;
++
++ SET16(fat_entry, content);
++ }
++ }
++ }
++
++ else if (p_fs->vol_type == FAT16) {
++
++ content &= 0x0000FFFF;
++
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1));
++ off = (loc << 1) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++
++ SET16_A(fat_entry, content);
++ }
++
++ else if (p_fs->vol_type == FAT32) {
++
++ content &= 0x0FFFFFFF;
++
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2));
++ off = (loc << 2) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++
++ content |= GET32_A(fat_entry) & 0xF0000000;
++
++ SET32_A(fat_entry, content);
++ }
++
++ else {
++
++ sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2));
++ off = (loc << 2) & p_bd->sector_size_mask;
++
++ fat_sector = FAT_getblk(sb, sec);
++ if (!fat_sector)
++ return -1;
++
++ fat_entry = &(fat_sector[off]);
++
++ SET32_A(fat_entry, content);
++ }
++
++ FAT_modify(sb, sec);
++ return 0;
++}
++
++UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ bp = FAT_cache_find(sb, sec);
++ if (bp != NULL) {
++ move_to_mru(bp, &p_fs->FAT_cache_lru_list);
++ return(bp->buf_bh->b_data);
++ }
++
++ bp = FAT_cache_get(sb, sec);
++
++ FAT_cache_remove_hash(bp);
++
++ bp->drv = p_fs->drv;
++ bp->sec = sec;
++ bp->flag = 0;
++
++ FAT_cache_insert_hash(sb, bp);
++
++ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) {
++ FAT_cache_remove_hash(bp);
++ bp->drv = -1;
++ bp->sec = ~0;
++ bp->flag = 0;
++ bp->buf_bh = NULL;
++
++ move_to_lru(bp, &p_fs->FAT_cache_lru_list);
++ return NULL;
++ }
++
++ return(bp->buf_bh->b_data);
++}
++
++void FAT_modify(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++
++ bp = FAT_cache_find(sb, sec);
++ if (bp != NULL) {
++ sector_write(sb, sec, bp->buf_bh, 0);
++ }
++}
++
++void FAT_release_all(struct super_block *sb)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&f_sem);
++
++ bp = p_fs->FAT_cache_lru_list.next;
++ while (bp != &p_fs->FAT_cache_lru_list) {
++ if (bp->drv == p_fs->drv) {
++ bp->drv = -1;
++ bp->sec = ~0;
++ bp->flag = 0;
++
++ if(bp->buf_bh) {
++ __brelse(bp->buf_bh);
++ bp->buf_bh = NULL;
++ }
++ }
++ bp = bp->next;
++ }
++
++ sm_V(&f_sem);
++}
++
++void FAT_sync(struct super_block *sb)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&f_sem);
++
++ bp = p_fs->FAT_cache_lru_list.next;
++ while (bp != &p_fs->FAT_cache_lru_list) {
++ if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) {
++ sync_dirty_buffer(bp->buf_bh);
++ bp->flag &= ~(DIRTYBIT);
++ }
++ bp = bp->next;
++ }
++
++ sm_V(&f_sem);
++}
++
++static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec)
++{
++ INT32 off;
++ BUF_CACHE_T *bp, *hp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1);
++
++ hp = &(p_fs->FAT_cache_hash_list[off]);
++ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) {
++ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) {
++
++ WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. "
++ "It will make system panic.\n");
++
++ touch_buffer(bp->buf_bh);
++ return(bp);
++ }
++ }
++ return(NULL);
++}
++
++static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ bp = p_fs->FAT_cache_lru_list.prev;
++
++
++ move_to_mru(bp, &p_fs->FAT_cache_lru_list);
++ return(bp);
++}
++
++static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp)
++{
++ INT32 off;
++ BUF_CACHE_T *hp;
++ FS_INFO_T *p_fs;
++
++ p_fs = &(EXFAT_SB(sb)->fs_info);
++ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1);
++
++ hp = &(p_fs->FAT_cache_hash_list[off]);
++ bp->hash_next = hp->hash_next;
++ bp->hash_prev = hp;
++ hp->hash_next->hash_prev = bp;
++ hp->hash_next = bp;
++}
++
++static void FAT_cache_remove_hash(BUF_CACHE_T *bp)
++{
++ (bp->hash_prev)->hash_next = bp->hash_next;
++ (bp->hash_next)->hash_prev = bp->hash_prev;
++}
++
++UINT8 *buf_getblk(struct super_block *sb, UINT32 sec)
++{
++ UINT8 *buf;
++
++ sm_P(&b_sem);
++
++ buf = __buf_getblk(sb, sec);
++
++ sm_V(&b_sem);
++
++ return(buf);
++}
++
++static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ bp = buf_cache_find(sb, sec);
++ if (bp != NULL) {
++ move_to_mru(bp, &p_fs->buf_cache_lru_list);
++ return(bp->buf_bh->b_data);
++ }
++
++ bp = buf_cache_get(sb, sec);
++
++ buf_cache_remove_hash(bp);
++
++ bp->drv = p_fs->drv;
++ bp->sec = sec;
++ bp->flag = 0;
++
++ buf_cache_insert_hash(sb, bp);
++
++ if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) {
++ buf_cache_remove_hash(bp);
++ bp->drv = -1;
++ bp->sec = ~0;
++ bp->flag = 0;
++ bp->buf_bh = NULL;
++
++ move_to_lru(bp, &p_fs->buf_cache_lru_list);
++ return NULL;
++ }
++
++ return(bp->buf_bh->b_data);
++
++}
++
++void buf_modify(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++
++ sm_P(&b_sem);
++
++ bp = buf_cache_find(sb, sec);
++ if (likely(bp != NULL)) {
++ sector_write(sb, sec, bp->buf_bh, 0);
++ }
++
++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec);
++
++ sm_V(&b_sem);
++}
++
++void buf_lock(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++
++ sm_P(&b_sem);
++
++ bp = buf_cache_find(sb, sec);
++ if (likely(bp != NULL)) bp->flag |= LOCKBIT;
++
++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec);
++
++ sm_V(&b_sem);
++}
++
++void buf_unlock(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++
++ sm_P(&b_sem);
++
++ bp = buf_cache_find(sb, sec);
++ if (likely(bp != NULL)) bp->flag &= ~(LOCKBIT);
++
++ WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec);
++
++ sm_V(&b_sem);
++}
++
++void buf_release(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&b_sem);
++
++ bp = buf_cache_find(sb, sec);
++ if (likely(bp != NULL)) {
++ bp->drv = -1;
++ bp->sec = ~0;
++ bp->flag = 0;
++
++ if(bp->buf_bh) {
++ __brelse(bp->buf_bh);
++ bp->buf_bh = NULL;
++ }
++
++ move_to_lru(bp, &p_fs->buf_cache_lru_list);
++ }
++
++ sm_V(&b_sem);
++}
++
++void buf_release_all(struct super_block *sb)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&b_sem);
++
++ bp = p_fs->buf_cache_lru_list.next;
++ while (bp != &p_fs->buf_cache_lru_list) {
++ if (bp->drv == p_fs->drv) {
++ bp->drv = -1;
++ bp->sec = ~0;
++ bp->flag = 0;
++
++ if(bp->buf_bh) {
++ __brelse(bp->buf_bh);
++ bp->buf_bh = NULL;
++ }
++ }
++ bp = bp->next;
++ }
++
++ sm_V(&b_sem);
++}
++
++void buf_sync(struct super_block *sb)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ sm_P(&b_sem);
++
++ bp = p_fs->buf_cache_lru_list.next;
++ while (bp != &p_fs->buf_cache_lru_list) {
++ if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) {
++ sync_dirty_buffer(bp->buf_bh);
++ bp->flag &= ~(DIRTYBIT);
++ }
++ bp = bp->next;
++ }
++
++ sm_V(&b_sem);
++}
++
++static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec)
++{
++ INT32 off;
++ BUF_CACHE_T *bp, *hp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1);
++
++ hp = &(p_fs->buf_cache_hash_list[off]);
++ for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) {
++ if ((bp->drv == p_fs->drv) && (bp->sec == sec)) {
++ touch_buffer(bp->buf_bh);
++ return(bp);
++ }
++ }
++ return(NULL);
++}
++
++static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec)
++{
++ BUF_CACHE_T *bp;
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ bp = p_fs->buf_cache_lru_list.prev;
++ while (bp->flag & LOCKBIT) bp = bp->prev;
++
++
++ move_to_mru(bp, &p_fs->buf_cache_lru_list);
++ return(bp);
++}
++
++static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp)
++{
++ INT32 off;
++ BUF_CACHE_T *hp;
++ FS_INFO_T *p_fs;
++
++ p_fs = &(EXFAT_SB(sb)->fs_info);
++ off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1);
++
++ hp = &(p_fs->buf_cache_hash_list[off]);
++ bp->hash_next = hp->hash_next;
++ bp->hash_prev = hp;
++ hp->hash_next->hash_prev = bp;
++ hp->hash_next = bp;
++}
++
++static void buf_cache_remove_hash(BUF_CACHE_T *bp)
++{
++ (bp->hash_prev)->hash_next = bp->hash_next;
++ (bp->hash_next)->hash_prev = bp->hash_prev;
++}
++
++static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list)
++{
++ bp->next = list->next;
++ bp->prev = list;
++ list->next->prev = bp;
++ list->next = bp;
++}
++
++static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list)
++{
++ bp->prev = list->prev;
++ bp->next = list;
++ list->prev->next = bp;
++ list->prev = bp;
++}
++
++static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list)
++{
++ bp->prev->next = bp->next;
++ bp->next->prev = bp->prev;
++ push_to_mru(bp, list);
++}
++
++static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list)
++{
++ bp->prev->next = bp->next;
++ bp->next->prev = bp->prev;
++ push_to_lru(bp, list);
++}
+diff --git a/drivers/staging/exfat/exfat_cache.h b/drivers/staging/exfat/exfat_cache.h
+new file mode 100644
+index 00000000..76745095
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_cache.h
+@@ -0,0 +1,63 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_CACHE_H
++#define _EXFAT_CACHE_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define LOCKBIT 0x01
++#define DIRTYBIT 0x02
++
++ typedef struct __BUF_CACHE_T {
++ struct __BUF_CACHE_T *next;
++ struct __BUF_CACHE_T *prev;
++ struct __BUF_CACHE_T *hash_next;
++ struct __BUF_CACHE_T *hash_prev;
++ INT32 drv;
++ UINT32 sec;
++ UINT32 flag;
++ struct buffer_head *buf_bh;
++ } BUF_CACHE_T;
++
++ INT32 buf_init(struct super_block *sb);
++ INT32 buf_shutdown(struct super_block *sb);
++ INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content);
++ INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content);
++ UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec);
++ void FAT_modify(struct super_block *sb, UINT32 sec);
++ void FAT_release_all(struct super_block *sb);
++ void FAT_sync(struct super_block *sb);
++ UINT8 *buf_getblk(struct super_block *sb, UINT32 sec);
++ void buf_modify(struct super_block *sb, UINT32 sec);
++ void buf_lock(struct super_block *sb, UINT32 sec);
++ void buf_unlock(struct super_block *sb, UINT32 sec);
++ void buf_release(struct super_block *sb, UINT32 sec);
++ void buf_release_all(struct super_block *sb);
++ void buf_sync(struct super_block *sb);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_config.h b/drivers/staging/exfat/exfat_config.h
+new file mode 100644
+index 00000000..b3cd0112
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_config.h
+@@ -0,0 +1,57 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_CONFIG_H
++#define _EXFAT_CONFIG_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define OS_NONOS 1
++#define OS_LINUX 2
++
++#define FFS_CONFIG_OS OS_LINUX
++
++#define FFS_CONFIG_LITTLE_ENDIAN 1
++#define FFS_CONFIG_LEGACY_32BIT_API 0
++#define FFS_CONFIG_LEGACY_32BIT_API 0
++#define FFS_CONFIG_SUPPORT_CP1250 1
++#define FFS_CONFIG_SUPPORT_CP1251 1
++#define FFS_CONFIG_SUPPORT_CP1252 1
++#define FFS_CONFIG_SUPPORT_CP1253 1
++#define FFS_CONFIG_SUPPORT_CP1254 1
++#define FFS_CONFIG_SUPPORT_CP1255 1
++#define FFS_CONFIG_SUPPORT_CP1256 1
++#define FFS_CONFIG_SUPPORT_CP1257 1
++#define FFS_CONFIG_SUPPORT_CP1258 1
++#define FFS_CONFIG_SUPPORT_CP874 1
++#define FFS_CONFIG_SUPPORT_CP932 1
++#define FFS_CONFIG_SUPPORT_CP936 1
++#define FFS_CONFIG_SUPPORT_CP949 1
++#define FFS_CONFIG_SUPPORT_CP950 1
++#define FFS_CONFIG_SUPPORT_UTF8 1
++#define EXFAT_CONFIG_DISCARD 1
++#define EXFAT_CONFIG_KERNEL_DEBUG 1
++#define EXFAT_CONFIG_DEBUG_MSG 0
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_data.c b/drivers/staging/exfat/exfat_data.c
+new file mode 100644
+index 00000000..6c58eac8
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_data.c
+@@ -0,0 +1,40 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_blkdev.h"
++#include "exfat_cache.h"
++#include "exfat_nls.h"
++#include "exfat_super.h"
++#include "exfat.h"
++
++FS_STRUCT_T fs_struct[MAX_DRIVE];
++
++DECLARE_MUTEX(f_sem);
++BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE];
++BUF_CACHE_T FAT_cache_lru_list;
++BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE];
++
++DECLARE_MUTEX(b_sem);
++BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE];
++BUF_CACHE_T buf_cache_lru_list;
++BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE];
+diff --git a/drivers/staging/exfat/exfat_data.h b/drivers/staging/exfat/exfat_data.h
+new file mode 100644
+index 00000000..27ca835c
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_data.h
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_DATA_H
++#define _EXFAT_DATA_H
++#include "exfat_config.h"
++#include "exfat_global.h"
++#ifdef __cplusplus
++extern "C" {
++#endif
++#define MAX_DEVICE 2
++#define MAX_DRIVE 2
++#define MAX_OPEN 20
++#define MAX_DENTRY 512
++#define FAT_CACHE_SIZE 128
++#define FAT_CACHE_HASH_SIZE 64
++#define BUF_CACHE_SIZE 256
++#define BUF_CACHE_HASH_SIZE 64
++#define DEFAULT_CODEPAGE 437
++#define DEFAULT_IOCHARSET "utf8"
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/drivers/staging/exfat/exfat_global.c b/drivers/staging/exfat/exfat_global.c
+new file mode 100644
+index 00000000..89934f9d
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_global.c
+@@ -0,0 +1,119 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++
++INT32 __wstrchr(UINT16 *str, UINT16 wchar)
++{
++ while (*str) {
++ if (*(str++) == wchar) return(1);
++ }
++ return(0);
++}
++
++INT32 __wstrlen(UINT16 *str)
++{
++ INT32 length = 0;
++
++ while (*(str++)) length++;
++ return(length);
++}
++
++#define BITMAP_LOC(v) ((v) >> 3)
++#define BITMAP_SHIFT(v) ((v) & 0x07)
++
++void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize)
++{
++ MEMSET(bitmap, 0xFF, mapsize);
++}
++
++void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize)
++{
++ MEMSET(bitmap, 0x0, mapsize);
++}
++
++INT32 Bitmap_test(UINT8 *bitmap, INT32 i)
++{
++ UINT8 data;
++
++ data = bitmap[BITMAP_LOC(i)];
++ if ((data >> BITMAP_SHIFT(i)) & 0x01) return(1);
++ return(0);
++}
++
++void Bitmap_set(UINT8 *bitmap, INT32 i)
++{
++ bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i));
++}
++
++void Bitmap_clear(UINT8 *bitmap, INT32 i)
++{
++ bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i));
++}
++
++void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits)
++{
++ INT32 i;
++
++ for (i = 0; i < nbits; i++) {
++ Bitmap_set(bitmap, offset+i);
++ }
++}
++
++void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits)
++{
++ INT32 i;
++
++ for (i = 0; i < nbits; i++) {
++ Bitmap_clear(bitmap, offset+i);
++ }
++}
++
++void my_itoa(INT8 *buf, INT32 v)
++{
++ INT32 mod[10];
++ INT32 i;
++
++ for (i = 0; i < 10; i++) {
++ mod[i] = (v % 10);
++ v = v / 10;
++ if (v == 0) break;
++ }
++
++ if (i == 10)
++ i--;
++
++ for (; i >= 0; i--) {
++ *buf = (UINT8) ('0' + mod[i]);
++ buf++;
++ }
++ *buf = '\0';
++}
++
++INT32 my_log2(UINT32 v)
++{
++ UINT32 bits = 0;
++
++ while (v > 1) {
++ if (v & 0x01) return(-1);
++ v >>= 1;
++ bits++;
++ }
++ return(bits);
++}
+diff --git a/drivers/staging/exfat/exfat_global.h b/drivers/staging/exfat/exfat_global.h
+new file mode 100644
+index 00000000..9d87deb2
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_global.h
+@@ -0,0 +1,127 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_GLOBAL_H
++#define _EXFAT_GLOBAL_H
++
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++
++#include "exfat_config.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++#ifndef FALSE
++#define FALSE 0
++#endif
++#ifndef OK
++#define OK 0
++#endif
++#ifndef FAIL
++#define FAIL 1
++#endif
++#ifndef NULL
++#define NULL 0
++#endif
++
++#define MIN(a, b) (((a) < (b)) ? (a) : (b))
++#define MAX(a, b) (((a) > (b)) ? (a) : (b))
++
++ typedef char INT8;
++ typedef short INT16;
++ typedef int INT32;
++ typedef long long INT64;
++ typedef unsigned char UINT8;
++ typedef unsigned short UINT16;
++ typedef unsigned int UINT32;
++ typedef unsigned long long UINT64;
++ typedef unsigned char BOOL;
++
++#ifdef MALLOC
++#undef MALLOC
++#endif
++#ifdef FREE
++#undef FREE
++#endif
++#ifdef MEMSET
++#undef MEMSET
++#endif
++#ifdef MEMCPY
++#undef MEMCPY
++#endif
++#ifdef MEMCMP
++#undef MEMCMP
++#endif
++
++#define MALLOC(size) kmalloc(size, GFP_KERNEL)
++#define FREE(mem) if (mem) kfree(mem)
++#define MEMSET(mem, value, size) memset(mem, value, size)
++#define MEMCPY(dest, src, size) memcpy(dest, src, size)
++#define MEMCMP(mem1, mem2, size) memcmp(mem1, mem2, size)
++#define COPY_DENTRY(dest, src) memcpy(dest, src, sizeof(DENTRY_T))
++
++#define STRCPY(dest, src) strcpy(dest, src)
++#define STRNCPY(dest, src, n) strncpy(dest, src, n)
++#define STRCAT(str1, str2) strcat(str1, str2)
++#define STRCMP(str1, str2) strcmp(str1, str2)
++#define STRNCMP(str1, str2, n) strncmp(str1, str2, n)
++#define STRLEN(str) strlen(str)
++
++ INT32 __wstrchr(UINT16 *str, UINT16 wchar);
++ INT32 __wstrlen(UINT16 *str);
++
++#define WSTRCHR(str, wchar) __wstrchr(str, wchar)
++#define WSTRLEN(str) __wstrlen(str)
++
++#if EXFAT_CONFIG_DEBUG_MSG
++#define PRINTK(...) \
++ do { \
++ printk("[EXFAT] " __VA_ARGS__); \
++ } while(0)
++#else
++#define PRINTK(...)
++#endif
++
++ void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize);
++ void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize);
++ INT32 Bitmap_test(UINT8 *bitmap, INT32 i);
++ void Bitmap_set(UINT8 *bitmap, INT32 i);
++ void Bitmap_clear(UINT8 *bitmpa, INT32 i);
++ void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits);
++ void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits);
++
++ void my_itoa(INT8 *buf, INT32 v);
++ INT32 my_log2(UINT32 v);
++
++#ifdef PRINT
++#undef PRINT
++#endif
++
++#define PRINT printk
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/drivers/staging/exfat/exfat_nls.c b/drivers/staging/exfat/exfat_nls.c
+new file mode 100644
+index 00000000..04ac3998
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_nls.c
+@@ -0,0 +1,347 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat_super.h"
++#include "exfat.h"
++
++#include <linux/nls.h>
++
++static UINT16 bad_dos_chars[] = {
++ 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D,
++ 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D,
++ 0
++};
++
++static UINT16 bad_uni_chars[] = {
++ 0x0022, 0x002A, 0x002F, 0x003A,
++ 0x003C, 0x003E, 0x003F, 0x005C, 0x007C,
++ 0
++};
++
++static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy);
++static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy);
++
++UINT16 nls_upper(struct super_block *sb, UINT16 a)
++{
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++
++ if (EXFAT_SB(sb)->options.casesensitive)
++ return(a);
++ if ((p_fs->vol_utbl)[get_col_index(a)] != NULL)
++ return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)];
++ else
++ return a;
++}
++
++INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b)
++{
++ return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH));
++}
++
++INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b)
++{
++ INT32 i;
++
++ for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) {
++ if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1);
++ if (*a == 0x0) return(0);
++ }
++ return(0);
++}
++
++void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy)
++{
++ INT32 i, j, len, lossy = FALSE;
++ UINT8 buf[MAX_CHARSET_SIZE];
++ UINT8 lower = 0, upper = 0;
++ UINT8 *dosname = p_dosname->name;
++ UINT16 *uniname = p_uniname->name;
++ UINT16 *p, *last_period;
++ struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
++
++ for (i = 0; i < DOS_NAME_LENGTH; i++) {
++ *(dosname+i) = ' ';
++ }
++
++ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) {
++ *(dosname) = '.';
++ p_dosname->name_case = 0x0;
++ if (p_lossy != NULL) *p_lossy = FALSE;
++ return;
++ }
++
++ if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) {
++ *(dosname) = '.';
++ *(dosname+1) = '.';
++ p_dosname->name_case = 0x0;
++ if (p_lossy != NULL) *p_lossy = FALSE;
++ return;
++ }
++
++ last_period = NULL;
++ for (p = uniname; *p; p++) {
++ if (*p == (UINT16) '.') last_period = p;
++ }
++
++ i = 0;
++ while (i < DOS_NAME_LENGTH) {
++ if (i == 8) {
++ if (last_period == NULL) break;
++
++ if (uniname <= last_period) {
++ if (uniname < last_period) lossy = TRUE;
++ uniname = last_period + 1;
++ }
++ }
++
++ if (*uniname == (UINT16) '\0') {
++ break;
++ } else if (*uniname == (UINT16) ' ') {
++ lossy = TRUE;
++ } else if (*uniname == (UINT16) '.') {
++ if (uniname < last_period) lossy = TRUE;
++ else i = 8;
++ } else if (WSTRCHR(bad_dos_chars, *uniname)) {
++ lossy = TRUE;
++ *(dosname+i) = '_';
++ i++;
++ } else {
++ len = convert_uni_to_ch(nls, buf, *uniname, &lossy);
++
++ if (len > 1) {
++ if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) {
++ break;
++ }
++ if ((i < 8) && ((i+len) > 8)) {
++ i = 8;
++ continue;
++ }
++
++ lower = 0xFF;
++
++ for (j = 0; j < len; j++, i++) {
++ *(dosname+i) = *(buf+j);
++ }
++ } else {
++ if ((*buf >= 'a') && (*buf <= 'z')) {
++ *(dosname+i) = *buf - ('a' - 'A');
++
++ if (i < 8) lower |= 0x08;
++ else lower |= 0x10;
++ } else if ((*buf >= 'A') && (*buf <= 'Z')) {
++ *(dosname+i) = *buf;
++
++ if (i < 8) upper |= 0x08;
++ else upper |= 0x10;
++ } else {
++ *(dosname+i) = *buf;
++ }
++ i++;
++ }
++ }
++
++ uniname++;
++ }
++
++ if (*dosname == 0xE5) *dosname = 0x05;
++ if (*uniname != 0x0) lossy = TRUE;
++
++ if (upper & lower) p_dosname->name_case = 0xFF;
++ else p_dosname->name_case = lower;
++
++ if (p_lossy != NULL) *p_lossy = lossy;
++}
++
++void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname)
++{
++ INT32 i = 0, j, n = 0;
++ UINT8 buf[DOS_NAME_LENGTH+2];
++ UINT8 *dosname = p_dosname->name;
++ UINT16 *uniname = p_uniname->name;
++ struct nls_table *nls = EXFAT_SB(sb)->nls_disk;
++
++ if (*dosname == 0x05) {
++ *buf = 0xE5;
++ i++;
++ n++;
++ }
++
++ for ( ; i < 8; i++, n++) {
++ if (*(dosname+i) == ' ') break;
++
++ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08))
++ *(buf+n) = *(dosname+i) + ('a' - 'A');
++ else
++ *(buf+n) = *(dosname+i);
++ }
++ if (*(dosname+8) != ' ') {
++ *(buf+n) = '.';
++ n++;
++ }
++
++ for (i = 8; i < DOS_NAME_LENGTH; i++, n++) {
++ if (*(dosname+i) == ' ') break;
++
++ if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10))
++ *(buf+n) = *(dosname+i) + ('a' - 'A');
++ else
++ *(buf+n) = *(dosname+i);
++ }
++ *(buf+n) = '\0';
++
++ i = j = 0;
++ while (j < (MAX_NAME_LENGTH-1)) {
++ if (*(buf+i) == '\0') break;
++
++ i += convert_ch_to_uni(nls, uniname, (buf+i), NULL);
++
++ uniname++;
++ j++;
++ }
++
++ *uniname = (UINT16) '\0';
++}
++
++void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname)
++{
++ INT32 i, j, len;
++ UINT8 buf[MAX_CHARSET_SIZE];
++ UINT16 *uniname = p_uniname->name;
++ struct nls_table *nls = EXFAT_SB(sb)->nls_io;
++
++ i = 0;
++ while (i < (MAX_NAME_LENGTH-1)) {
++ if (*uniname == (UINT16) '\0') break;
++
++ len = convert_uni_to_ch(nls, buf, *uniname, NULL);
++
++ if (len > 1) {
++ for (j = 0; j < len; j++)
++ *p_cstring++ = (INT8) *(buf+j);
++ } else {
++ *p_cstring++ = (INT8) *buf;
++ }
++
++ uniname++;
++ i++;
++ }
++
++ *p_cstring = '\0';
++}
++
++void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy)
++{
++ INT32 i, j, lossy = FALSE;
++ UINT8 *end_of_name;
++ UINT16 upname[MAX_NAME_LENGTH];
++ UINT16 *uniname = p_uniname->name;
++ struct nls_table *nls = EXFAT_SB(sb)->nls_io;
++
++ end_of_name = p_cstring + STRLEN((INT8 *) p_cstring);
++
++ while (*(--end_of_name) == ' ') {
++ if (end_of_name < p_cstring) break;
++ }
++ *(++end_of_name) = '\0';
++
++ if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) {
++ while (*(--end_of_name) == '.') {
++ if (end_of_name < p_cstring) break;
++ }
++ *(++end_of_name) = '\0';
++ }
++
++ if (*p_cstring == '\0')
++ lossy = TRUE;
++
++ i = j = 0;
++ while (j < (MAX_NAME_LENGTH-1)) {
++ if (*(p_cstring+i) == '\0') break;
++
++ i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy);
++
++ if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname))
++ lossy = TRUE;
++
++ *(upname+j) = nls_upper(sb, *uniname);
++
++ uniname++;
++ j++;
++ }
++
++ if (*(p_cstring+i) != '\0')
++ lossy = TRUE;
++ *uniname = (UINT16) '\0';
++
++ p_uniname->name_len = j;
++ p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT);
++
++ if (p_lossy != NULL)
++ *p_lossy = lossy;
++}
++
++static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy)
++{
++ int len;
++
++ *uni = 0x0;
++
++ if (ch[0] < 0x80) {
++ *uni = (UINT16) ch[0];
++ return(1);
++ }
++
++ if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) {
++ printk("%s: fail to use nls \n", __func__);
++ if (lossy != NULL)
++ *lossy = TRUE;
++ *uni = (UINT16) '_';
++ if (!strcmp(nls->charset, "utf8")) return(1);
++ else return(2);
++ }
++
++ return(len);
++}
++
++static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy)
++{
++ int len;
++
++ ch[0] = 0x0;
++
++ if (uni < 0x0080) {
++ ch[0] = (UINT8) uni;
++ return(1);
++ }
++
++ if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) {
++ printk("%s: fail to use nls \n", __func__);
++ if (lossy != NULL) *lossy = TRUE;
++ ch[0] = '_';
++ return(1);
++ }
++
++ return(len);
++
++}
+diff --git a/drivers/staging/exfat/exfat_nls.h b/drivers/staging/exfat/exfat_nls.h
+new file mode 100644
+index 00000000..8a01d269
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_nls.h
+@@ -0,0 +1,68 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_NLS_H
++#define _EXFAT_NLS_H
++
++#include <linux/nls.h>
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_api.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define NUM_UPCASE 2918
++
++#define DOS_CUR_DIR_NAME ". "
++#define DOS_PAR_DIR_NAME ".. "
++
++#if (FFS_CONFIG_LITTLE_ENDIAN == 1)
++#define UNI_CUR_DIR_NAME ".\0"
++#define UNI_PAR_DIR_NAME ".\0.\0"
++#else
++#define UNI_CUR_DIR_NAME "\0."
++#define UNI_PAR_DIR_NAME "\0.\0."
++#endif
++
++
++typedef struct {
++ UINT8 name[DOS_NAME_LENGTH];
++ UINT8 name_case;
++} DOS_NAME_T;
++
++typedef struct {
++ UINT16 name[MAX_NAME_LENGTH];
++ UINT16 name_hash;
++ UINT8 name_len;
++} UNI_NAME_T;
++
++UINT16 nls_upper(struct super_block *sb, UINT16 a);
++INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b);
++INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b);
++void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy);
++void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname);
++void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname);
++void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy);
++
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/drivers/staging/exfat/exfat_oal.c b/drivers/staging/exfat/exfat_oal.c
+new file mode 100644
+index 00000000..717e4bf8
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_oal.c
+@@ -0,0 +1,146 @@
++/* Some of the source code in this file came from "linux/fs/fat/misc.c". */
++/*
++ * linux/fs/fat/misc.c
++ *
++ * Written 1992,1993 by Werner Almesberger
++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
++ */
++
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include <linux/semaphore.h>
++#include <linux/time.h>
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_api.h"
++#include "exfat_oal.h"
++
++DECLARE_MUTEX(z_sem);
++
++INT32 sm_init(struct semaphore *sm)
++{
++ sema_init(sm, 1);
++ return(0);
++}
++
++INT32 sm_P(struct semaphore *sm)
++{
++ down(sm);
++ return 0;
++}
++
++void sm_V(struct semaphore *sm)
++{
++ up(sm);
++}
++
++extern struct timezone sys_tz;
++
++#define UNIX_SECS_1980 315532800L
++
++#if BITS_PER_LONG == 64
++#define UNIX_SECS_2108 4354819200L
++#endif
++
++#define DAYS_DELTA_DECADE (365 * 10 + 2)
++#define NO_LEAP_YEAR_2100 (120)
++#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100)
++
++#define SECS_PER_MIN (60)
++#define SECS_PER_HOUR (60 * SECS_PER_MIN)
++#define SECS_PER_DAY (24 * SECS_PER_HOUR)
++
++#define MAKE_LEAP_YEAR(leap_year, year) \
++ do { \
++ if (unlikely(year > NO_LEAP_YEAR_2100)) \
++ leap_year = ((year + 3) / 4) - 1; \
++ else \
++ leap_year = ((year + 3) / 4); \
++ } while(0)
++
++
++
++static time_t accum_days_in_year[] = {
++ 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0,
++};
++
++
++TIMESTAMP_T *tm_current(TIMESTAMP_T *tp)
++{
++ struct timespec ts = CURRENT_TIME_SEC;
++ time_t second = ts.tv_sec;
++ time_t day, leap_day, month, year;
++
++ second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
++
++ if (second < UNIX_SECS_1980) {
++ tp->sec = 0;
++ tp->min = 0;
++ tp->hour = 0;
++ tp->day = 1;
++ tp->mon = 1;
++ tp->year = 0;
++ return(tp);
++ }
++#if BITS_PER_LONG == 64
++ if (second >= UNIX_SECS_2108) {
++ tp->sec = 59;
++ tp->min = 59;
++ tp->hour = 23;
++ tp->day = 31;
++ tp->mon = 12;
++ tp->year = 127;
++ return(tp);
++ }
++#endif
++
++ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
++ year = day / 365;
++
++ MAKE_LEAP_YEAR(leap_day, year);
++ if (year * 365 + leap_day > day)
++ year--;
++
++ MAKE_LEAP_YEAR(leap_day, year);
++
++ day -= year * 365 + leap_day;
++
++ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
++ month = 2;
++ } else {
++ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
++ day--;
++ for (month = 1; month < 12; month++) {
++ if (accum_days_in_year[month + 1] > day)
++ break;
++ }
++ }
++ day -= accum_days_in_year[month];
++
++ tp->sec = second % SECS_PER_MIN;
++ tp->min = (second / SECS_PER_MIN) % 60;
++ tp->hour = (second / SECS_PER_HOUR) % 24;
++ tp->day = day + 1;
++ tp->mon = month;
++ tp->year = year;
++
++ return(tp);
++}
+diff --git a/drivers/staging/exfat/exfat_oal.h b/drivers/staging/exfat/exfat_oal.h
+new file mode 100644
+index 00000000..4c7d95b2
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_oal.h
+@@ -0,0 +1,53 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_OAL_H
++#define _EXFAT_OAL_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include <linux/version.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++ typedef struct {
++ UINT16 sec;
++ UINT16 min;
++ UINT16 hour;
++ UINT16 day;
++ UINT16 mon;
++ UINT16 year;
++ } TIMESTAMP_T;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
++#define DECLARE_MUTEX(m) DEFINE_SEMAPHORE(m)
++#endif
++
++ INT32 sm_init(struct semaphore *sm);
++ INT32 sm_P(struct semaphore *sm);
++ void sm_V(struct semaphore *sm);
++
++ TIMESTAMP_T *tm_current(TIMESTAMP_T *tm);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_part.h b/drivers/staging/exfat/exfat_part.h
+new file mode 100644
+index 00000000..cc6db65c
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_part.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_PART_H
++#define _EXFAT_PART_H
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_api.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define MBR_SIGNATURE 0xAA55
++ typedef struct {
++ UINT8 boot_code[446];
++ UINT8 partition[64];
++ UINT8 signature[2];
++ } MBR_SECTOR_T;
++
++ typedef struct {
++ UINT8 def_boot;
++ UINT8 bgn_chs[3];
++ UINT8 sys_type;
++ UINT8 end_chs[3];
++ UINT8 start_sector[4];
++ UINT8 num_sectors[4];
++ } PART_ENTRY_T;
++
++ INT32 ffsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec);
++ INT32 ffsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec);
++ INT32 ffsGetDevInfo(INT32 dev, DEV_INFO_T *info);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c
+new file mode 100644
+index 00000000..8c937631
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_super.c
+@@ -0,0 +1,2154 @@
++/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */
++/*
++ * linux/fs/fat/file.c
++ *
++ * Written 1992,1993 by Werner Almesberger
++ *
++ * regular file handling primitives for fat-based filesystems
++ */
++
++/*
++ * linux/fs/fat/inode.c
++ *
++ * Written 1992,1993 by Werner Almesberger
++ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
++ * Rewritten for the constant inumbers support by Al Viro
++ *
++ * Fixes:
++ *
++ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
++ */
++
++/*
++ * linux/fs/fat/misc.c
++ *
++ * Written 1992,1993 by Werner Almesberger
++ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
++ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
++ */
++
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/time.h>
++#include <linux/slab.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
++#include <linux/smp_lock.h>
++#endif
++#include <linux/seq_file.h>
++#include <linux/pagemap.h>
++#include <linux/mpage.h>
++#include <linux/buffer_head.h>
++#include <linux/exportfs.h>
++#include <linux/mount.h>
++#include <linux/vfs.h>
++#include <linux/parser.h>
++#include <linux/uio.h>
++#include <linux/writeback.h>
++#include <linux/log2.h>
++#include <linux/hash.h>
++#include <linux/backing-dev.h>
++#include <linux/sched.h>
++#include <linux/fs_struct.h>
++#include <linux/namei.h>
++#include <asm/current.h>
++#include <asm/unaligned.h>
++
++#include "exfat_version.h"
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_blkdev.h"
++#include "exfat_cache.h"
++#include "exfat_part.h"
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat.h"
++
++#include "exfat_super.h"
++
++static struct kmem_cache *exfat_inode_cachep;
++
++static int exfat_default_codepage = DEFAULT_CODEPAGE;
++static char exfat_default_iocharset[] = DEFAULT_IOCHARSET;
++
++extern struct timezone sys_tz;
++
++#define CHECK_ERR(x) BUG_ON(x)
++#define ELAPSED_TIME 0
++
++#if (ELAPSED_TIME == 1)
++#include <linux/time.h>
++
++static UINT32 __t1, __t2;
++static UINT32 get_current_msec(void)
++{
++ struct timeval tm;
++ do_gettimeofday(&tm);
++ return((UINT32)(tm.tv_sec*1000000 + tm.tv_usec));
++}
++#define TIME_START() do {__t1 = get_current_msec();} while (0)
++#define TIME_END() do {__t2 = get_current_msec();} while (0)
++#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1));} while (0)
++#else
++#define TIME_START()
++#define TIME_END()
++#define PRINT_TIME(n)
++#endif
++
++#define UNIX_SECS_1980 315532800L
++
++#if BITS_PER_LONG == 64
++#define UNIX_SECS_2108 4354819200L
++#endif
++#define DAYS_DELTA_DECADE (365 * 10 + 2)
++#define NO_LEAP_YEAR_2100 (120)
++#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100)
++
++#define SECS_PER_MIN (60)
++#define SECS_PER_HOUR (60 * SECS_PER_MIN)
++#define SECS_PER_DAY (24 * SECS_PER_HOUR)
++
++#define MAKE_LEAP_YEAR(leap_year, year) \
++ do { \
++ if (unlikely(year > NO_LEAP_YEAR_2100)) \
++ leap_year = ((year + 3) / 4) - 1; \
++ else \
++ leap_year = ((year + 3) / 4); \
++ } while(0)
++
++static time_t accum_days_in_year[] = {
++ 0, 0, 31, 59, 90,120,151,181,212,243,273,304,334, 0, 0, 0,
++};
++
++static void _exfat_truncate(struct inode *inode, loff_t old_size);
++
++void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts,
++ DATE_TIME_T *tp)
++{
++ time_t year = tp->Year;
++ time_t ld;
++
++ MAKE_LEAP_YEAR(ld, year);
++
++ if (IS_LEAP_YEAR(year) && (tp->Month) > 2)
++ ld++;
++
++ ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN
++ + tp->Hour * SECS_PER_HOUR
++ + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY
++ + sys_tz.tz_minuteswest * SECS_PER_MIN;
++ ts->tv_nsec = 0;
++}
++
++void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts,
++ DATE_TIME_T *tp)
++{
++ time_t second = ts->tv_sec;
++ time_t day, month, year;
++ time_t ld;
++
++ second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
++
++ if (second < UNIX_SECS_1980) {
++ tp->Second = 0;
++ tp->Minute = 0;
++ tp->Hour = 0;
++ tp->Day = 1;
++ tp->Month = 1;
++ tp->Year = 0;
++ return;
++ }
++#if (BITS_PER_LONG == 64)
++ if (second >= UNIX_SECS_2108) {
++ tp->Second = 59;
++ tp->Minute = 59;
++ tp->Hour = 23;
++ tp->Day = 31;
++ tp->Month = 12;
++ tp->Year = 127;
++ return;
++ }
++#endif
++ day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
++ year = day / 365;
++ MAKE_LEAP_YEAR(ld, year);
++ if (year * 365 + ld > day)
++ year--;
++
++ MAKE_LEAP_YEAR(ld, year);
++ day -= year * 365 + ld;
++
++ if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
++ month = 2;
++ } else {
++ if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
++ day--;
++ for (month = 1; month < 12; month++) {
++ if (accum_days_in_year[month + 1] > day)
++ break;
++ }
++ }
++ day -= accum_days_in_year[month];
++
++ tp->Second = second % SECS_PER_MIN;
++ tp->Minute = (second / SECS_PER_MIN) % 60;
++ tp->Hour = (second / SECS_PER_HOUR) % 24;
++ tp->Day = day + 1;
++ tp->Month = month;
++ tp->Year = year;
++}
++
++static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
++#else
++static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
++#endif
++static int exfat_sync_inode(struct inode *inode);
++static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos);
++static void exfat_detach(struct inode *inode);
++static void exfat_attach(struct inode *inode, loff_t i_pos);
++static inline unsigned long exfat_hash(loff_t i_pos);
++static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
++static void exfat_write_super(struct super_block *sb);
++
++static void __lock_super(struct super_block *sb)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
++ lock_super(sb);
++#else
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ mutex_lock(&sbi->s_lock);
++#endif
++}
++
++static void __unlock_super(struct super_block *sb)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
++ unlock_super(sb);
++#else
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ mutex_unlock(&sbi->s_lock);
++#endif
++}
++
++static int __is_sb_dirty(struct super_block *sb)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
++ return sb->s_dirt;
++#else
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ return sbi->s_dirt;
++#endif
++}
++
++static void __set_sb_clean(struct super_block *sb)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
++ sb->s_dirt = 0;
++#else
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ sbi->s_dirt = 0;
++#endif
++}
++
++static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++ struct inode *inode = filp->f_path.dentry->d_inode;
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++ BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info);
++ DIR_ENTRY_T de;
++ unsigned long inum;
++ loff_t cpos;
++ int err = 0;
++
++ __lock_super(sb);
++
++ cpos = filp->f_pos;
++ if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) {
++ while (cpos < 2) {
++ if (inode->i_ino == EXFAT_ROOT_INO)
++ inum = EXFAT_ROOT_INO;
++ else if (cpos == 0)
++ inum = inode->i_ino;
++ else
++ inum = parent_ino(filp->f_path.dentry);
++
++ if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0)
++ goto out;
++ cpos++;
++ filp->f_pos++;
++ }
++ if (cpos == 2) {
++ cpos = 0;
++ }
++ }
++ if (cpos & (DENTRY_SIZE - 1)) {
++ err = -ENOENT;
++ goto out;
++ }
++
++get_new:
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++ EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS;
++
++ err = FsReadDir(inode, &de);
++ if (err) {
++ if (err == FFS_MEDIAERR) {
++ cpos += 1 << p_bd->sector_size_bits;
++ cpos &= ~((1 << p_bd->sector_size_bits)-1);
++ }
++
++ err = -EIO;
++ goto end_of_dir;
++ }
++
++ cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS;
++
++ if (!de.Name[0])
++ goto end_of_dir;
++
++ if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) {
++ inum = inode->i_ino;
++ } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) {
++ inum = parent_ino(filp->f_path.dentry);
++ } else {
++ loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) |
++ ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff);
++
++ struct inode *tmp = exfat_iget(sb, i_pos);
++ if (tmp) {
++ inum = tmp->i_ino;
++ iput(tmp);
++ } else {
++ inum = iunique(sb, EXFAT_ROOT_INO);
++ }
++ }
++
++ if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum,
++ (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0)
++ goto out;
++
++ filp->f_pos = cpos;
++ goto get_new;
++
++end_of_dir:
++ filp->f_pos = cpos;
++out:
++ __unlock_super(sb);
++ return err;
++}
++
++static int exfat_ioctl_volume_id(struct inode *dir)
++{
++ struct super_block *sb = dir->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++
++ return p_fs->vol_id;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++static int exfat_generic_ioctl(struct inode *inode, struct file *filp,
++ unsigned int cmd, unsigned long arg)
++#else
++static long exfat_generic_ioctl(struct file *filp,
++ unsigned int cmd, unsigned long arg)
++#endif
++{
++#if EXFAT_CONFIG_KERNEL_DEBUG
++#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36))
++ struct inode *inode = filp->f_dentry->d_inode;
++#endif
++ unsigned int flags;
++#endif
++
++ switch (cmd) {
++ case EXFAT_IOCTL_GET_VOLUME_ID:
++ return exfat_ioctl_volume_id(inode);
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ case EXFAT_IOC_GET_DEBUGFLAGS: {
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++
++ flags = sbi->debug_flags;
++ return put_user(flags, (int __user *)arg);
++ }
++ case EXFAT_IOC_SET_DEBUGFLAGS: {
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ if (get_user(flags, (int __user *) arg))
++ return -EFAULT;
++
++ __lock_super(sb);
++ sbi->debug_flags = flags;
++ __unlock_super(sb);
++
++ return 0;
++ }
++#endif
++ default:
++ return -ENOTTY;
++ }
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++static int exfat_file_fsync(struct file *filp, int datasync)
++{
++ struct inode *inode = filp->f_mapping->host;
++ struct super_block *sb = inode->i_sb;
++ int res, err;
++
++ res = generic_file_fsync(filp, datasync);
++ err = FsSyncVol(sb, 1);
++
++ return res ? res : err;
++}
++#endif
++
++const struct file_operations exfat_dir_operations = {
++ .llseek = generic_file_llseek,
++ .read = generic_read_dir,
++ .readdir = exfat_readdir,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++ .ioctl = exfat_generic_ioctl,
++ .fsync = exfat_file_fsync,
++#else
++ .unlocked_ioctl = exfat_generic_ioctl,
++ .fsync = generic_file_fsync,
++#endif
++};
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
++static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
++ bool excl)
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
++ struct nameidata *nd)
++#else
++static int exfat_create(struct inode *dir, struct dentry *dentry, int mode,
++ struct nameidata *nd)
++#endif
++{
++ struct super_block *sb = dir->i_sb;
++ struct inode *inode;
++ struct timespec ts;
++ FILE_ID_T fid;
++ loff_t i_pos;
++ int err;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_create entered\n");
++
++ ts = CURRENT_TIME_SEC;
++
++ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_REGULAR, &fid);
++ if (err) {
++ if (err == FFS_INVALIDPATH)
++ err = -EINVAL;
++ else if (err == FFS_FILEEXIST)
++ err = -EEXIST;
++ else if (err == FFS_FULL)
++ err = -ENOSPC;
++ else if (err == FFS_NAMETOOLONG)
++ err = -ENAMETOOLONG;
++ else
++ err = -EIO;
++ goto out;
++ }
++ dir->i_version++;
++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts;
++ if (IS_DIRSYNC(dir))
++ (void) exfat_sync_inode(dir);
++ else
++ mark_inode_dirty(dir);
++
++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
++
++ inode = exfat_build_inode(sb, &fid, i_pos);
++ if (IS_ERR(inode)) {
++ err = PTR_ERR(inode);
++ goto out;
++ }
++ inode->i_version++;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
++
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++ d_instantiate(dentry, inode);
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_create exited\n");
++ return err;
++}
++
++static int exfat_find(struct inode *dir, struct qstr *qname,
++ FILE_ID_T *fid)
++{
++ int err;
++
++ if (qname->len == 0)
++ return -ENOENT;
++
++ err = FsLookupFile(dir, (UINT8 *) qname->name, fid);
++ if (err)
++ return -ENOENT;
++
++ return 0;
++}
++
++static int exfat_d_anon_disconn(struct dentry *dentry)
++{
++ return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED);
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
++static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
++ unsigned int flags)
++#else
++static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
++ struct nameidata *nd)
++#endif
++{
++ struct super_block *sb = dir->i_sb;
++ struct inode *inode;
++ struct dentry *alias;
++ int err;
++ FILE_ID_T fid;
++ loff_t i_pos;
++ UINT64 ret;
++ mode_t i_mode;
++
++ __lock_super(sb);
++ PRINTK("exfat_lookup entered\n");
++ err = exfat_find(dir, &dentry->d_name, &fid);
++ if (err) {
++ if (err == -ENOENT) {
++ inode = NULL;
++ goto out;
++ }
++ goto error;
++ }
++
++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
++ inode = exfat_build_inode(sb, &fid, i_pos);
++ if (IS_ERR(inode)) {
++ err = PTR_ERR(inode);
++ goto error;
++ }
++
++ i_mode = inode->i_mode;
++ if (S_ISLNK(i_mode)) {
++ EXFAT_I(inode)->target = MALLOC(i_size_read(inode)+1);
++ if (!EXFAT_I(inode)->target) {
++ err = -ENOMEM;
++ goto error;
++ }
++ FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret);
++ *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0';
++ }
++
++ alias = d_find_alias(inode);
++ if (alias && !exfat_d_anon_disconn(alias)) {
++ CHECK_ERR(d_unhashed(alias));
++ if (!S_ISDIR(i_mode))
++ d_move(alias, dentry);
++ iput(inode);
++ __unlock_super(sb);
++ PRINTK("exfat_lookup exited 1\n");
++ return alias;
++ } else {
++ dput(alias);
++ }
++out:
++ __unlock_super(sb);
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
++ dentry->d_op = sb->s_root->d_op;
++ dentry = d_splice_alias(inode, dentry);
++ if (dentry) {
++ dentry->d_op = sb->s_root->d_op;
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++ }
++#else
++ dentry = d_splice_alias(inode, dentry);
++ if (dentry)
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++#endif
++ PRINTK("exfat_lookup exited 2\n");
++ return dentry;
++
++error:
++ __unlock_super(sb);
++ PRINTK("exfat_lookup exited 3\n");
++ return ERR_PTR(err);
++}
++
++static int exfat_unlink(struct inode *dir, struct dentry *dentry)
++{
++ struct inode *inode = dentry->d_inode;
++ struct super_block *sb = dir->i_sb;
++ struct timespec ts;
++ int err;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_unlink entered\n");
++
++ ts = CURRENT_TIME_SEC;
++
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++
++ err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid));
++ if (err) {
++ if (err == FFS_PERMISSIONERR)
++ err = -EPERM;
++ else
++ err = -EIO;
++ goto out;
++ }
++ dir->i_version++;
++ dir->i_mtime = dir->i_atime = ts;
++ if (IS_DIRSYNC(dir))
++ (void) exfat_sync_inode(dir);
++ else
++ mark_inode_dirty(dir);
++
++ clear_nlink(inode);
++ inode->i_mtime = inode->i_atime = ts;
++ exfat_detach(inode);
++ remove_inode_hash(inode);
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_unlink exited\n");
++ return err;
++}
++
++static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target)
++{
++ struct super_block *sb = dir->i_sb;
++ struct inode *inode;
++ struct timespec ts;
++ FILE_ID_T fid;
++ loff_t i_pos;
++ int err;
++ UINT64 len = (UINT64) strlen(target);
++ UINT64 ret;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_symlink entered\n");
++
++ ts = CURRENT_TIME_SEC;
++
++ err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_SYMLINK, &fid);
++ if (err) {
++ if (err == FFS_INVALIDPATH)
++ err = -EINVAL;
++ else if (err == FFS_FILEEXIST)
++ err = -EEXIST;
++ else if (err == FFS_FULL)
++ err = -ENOSPC;
++ else
++ err = -EIO;
++ goto out;
++ }
++
++ err = FsWriteFile(dir, &fid, (char *) target, len, &ret);
++
++ if (err) {
++ FsRemoveFile(dir, &fid);
++
++ if (err == FFS_FULL)
++ err = -ENOSPC;
++ else
++ err = -EIO;
++ goto out;
++ }
++
++ dir->i_version++;
++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts;
++ if (IS_DIRSYNC(dir))
++ (void) exfat_sync_inode(dir);
++ else
++ mark_inode_dirty(dir);
++
++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
++
++ inode = exfat_build_inode(sb, &fid, i_pos);
++ if (IS_ERR(inode)) {
++ err = PTR_ERR(inode);
++ goto out;
++ }
++ inode->i_version++;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
++
++ EXFAT_I(inode)->target = MALLOC(len+1);
++ if (!EXFAT_I(inode)->target) {
++ err = -ENOMEM;
++ goto out;
++ }
++ MEMCPY(EXFAT_I(inode)->target, target, len+1);
++
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++ d_instantiate(dentry, inode);
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_symlink exited\n");
++ return err;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
++#else
++static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++#endif
++{
++ struct super_block *sb = dir->i_sb;
++ struct inode *inode;
++ struct timespec ts;
++ FILE_ID_T fid;
++ loff_t i_pos;
++ int err;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_mkdir entered\n");
++
++ ts = CURRENT_TIME_SEC;
++
++ err = FsCreateDir(dir, (UINT8 *) dentry->d_name.name, &fid);
++ if (err) {
++ if (err == FFS_INVALIDPATH)
++ err = -EINVAL;
++ else if (err == FFS_FILEEXIST)
++ err = -EEXIST;
++ else if (err == FFS_FULL)
++ err = -ENOSPC;
++ else if (err == FFS_NAMETOOLONG)
++ err = -ENAMETOOLONG;
++ else
++ err = -EIO;
++ goto out;
++ }
++ dir->i_version++;
++ dir->i_ctime = dir->i_mtime = dir->i_atime = ts;
++ if (IS_DIRSYNC(dir))
++ (void) exfat_sync_inode(dir);
++ else
++ mark_inode_dirty(dir);
++ inc_nlink(dir);
++
++ i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff);
++
++ inode = exfat_build_inode(sb, &fid, i_pos);
++ if (IS_ERR(inode)) {
++ err = PTR_ERR(inode);
++ goto out;
++ }
++ inode->i_version++;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
++
++ dentry->d_time = dentry->d_parent->d_inode->i_version;
++ d_instantiate(dentry, inode);
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_mkdir exited\n");
++ return err;
++}
++
++static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ struct inode *inode = dentry->d_inode;
++ struct super_block *sb = dir->i_sb;
++ struct timespec ts;
++ int err;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_rmdir entered\n");
++
++ ts = CURRENT_TIME_SEC;
++
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++
++ err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid));
++ if (err) {
++ if (err == FFS_INVALIDPATH)
++ err = -EINVAL;
++ else if (err == FFS_FILEEXIST)
++ err = -ENOTEMPTY;
++ else if (err == FFS_NOTFOUND)
++ err = -ENOENT;
++ else if (err == FFS_DIRBUSY)
++ err = -EBUSY;
++ else
++ err = -EIO;
++ goto out;
++ }
++ dir->i_version++;
++ dir->i_mtime = dir->i_atime = ts;
++ if (IS_DIRSYNC(dir))
++ (void) exfat_sync_inode(dir);
++ else
++ mark_inode_dirty(dir);
++ drop_nlink(dir);
++
++ clear_nlink(inode);
++ inode->i_mtime = inode->i_atime = ts;
++ exfat_detach(inode);
++ remove_inode_hash(inode);
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_rmdir exited\n");
++ return err;
++}
++
++static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
++ struct inode *new_dir, struct dentry *new_dentry)
++{
++ struct inode *old_inode, *new_inode;
++ struct super_block *sb = old_dir->i_sb;
++ struct timespec ts;
++ loff_t i_pos;
++ int err;
++
++ __lock_super(sb);
++
++ PRINTK("exfat_rename entered\n");
++
++ old_inode = old_dentry->d_inode;
++ new_inode = new_dentry->d_inode;
++
++ ts = CURRENT_TIME_SEC;
++
++ EXFAT_I(old_inode)->fid.size = i_size_read(old_inode);
++
++ err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry);
++ if (err) {
++ if (err == FFS_PERMISSIONERR)
++ err = -EPERM;
++ else if (err == FFS_INVALIDPATH)
++ err = -EINVAL;
++ else if (err == FFS_FILEEXIST)
++ err = -EEXIST;
++ else if (err == FFS_NOTFOUND)
++ err = -ENOENT;
++ else if (err == FFS_FULL)
++ err = -ENOSPC;
++ else
++ err = -EIO;
++ goto out;
++ }
++ new_dir->i_version++;
++ new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts;
++ if (IS_DIRSYNC(new_dir))
++ (void) exfat_sync_inode(new_dir);
++ else
++ mark_inode_dirty(new_dir);
++
++ i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) |
++ (EXFAT_I(old_inode)->fid.entry & 0xffffffff);
++
++ exfat_detach(old_inode);
++ exfat_attach(old_inode, i_pos);
++ if (IS_DIRSYNC(new_dir))
++ (void) exfat_sync_inode(old_inode);
++ else
++ mark_inode_dirty(old_inode);
++
++ if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) {
++ drop_nlink(old_dir);
++ if (!new_inode) inc_nlink(new_dir);
++ }
++
++ old_dir->i_version++;
++ old_dir->i_ctime = old_dir->i_mtime = ts;
++ if (IS_DIRSYNC(old_dir))
++ (void) exfat_sync_inode(old_dir);
++ else
++ mark_inode_dirty(old_dir);
++
++ if (new_inode) {
++ exfat_detach(new_inode);
++ drop_nlink(new_inode);
++ if (S_ISDIR(new_inode->i_mode))
++ drop_nlink(new_inode);
++ new_inode->i_ctime = ts;
++ }
++
++out:
++ __unlock_super(sb);
++ PRINTK("exfat_rename exited\n");
++ return err;
++}
++
++static int exfat_cont_expand(struct inode *inode, loff_t size)
++{
++ struct address_space *mapping = inode->i_mapping;
++ loff_t start = i_size_read(inode), count = size - i_size_read(inode);
++ int err, err2;
++
++ if ((err = generic_cont_expand_simple(inode, size)) != 0)
++ return err;
++
++ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
++ mark_inode_dirty(inode);
++
++ if (IS_SYNC(inode)) {
++ err = filemap_fdatawrite_range(mapping, start, start + count - 1);
++ err2 = sync_mapping_buffers(mapping);
++ err = (err)?(err):(err2);
++ err2 = write_inode_now(inode, 1);
++ err = (err)?(err):(err2);
++ if (!err) {
++ err = filemap_fdatawait_range(mapping, start, start + count - 1);
++ }
++ }
++ return err;
++}
++
++static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
++{
++ mode_t allow_utime = sbi->options.allow_utime;
++
++ if (current_fsuid() != inode->i_uid) {
++ if (in_group_p(inode->i_gid))
++ allow_utime >>= 3;
++ if (allow_utime & MAY_WRITE)
++ return 1;
++ }
++
++ return 0;
++}
++
++static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
++ struct inode *inode, umode_t *mode_ptr)
++{
++ mode_t i_mode, mask, perm;
++
++ i_mode = inode->i_mode;
++
++ if (S_ISREG(i_mode) || S_ISLNK(i_mode))
++ mask = sbi->options.fs_fmask;
++ else
++ mask = sbi->options.fs_dmask;
++
++ perm = *mode_ptr & ~(S_IFMT | mask);
++
++ if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO)))
++ return -EPERM;
++
++ if (exfat_mode_can_hold_ro(inode)) {
++ if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
++ return -EPERM;
++ } else {
++ if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
++ return -EPERM;
++ }
++
++ *mode_ptr &= S_IFMT | perm;
++
++ return 0;
++}
++
++static int exfat_setattr(struct dentry *dentry, struct iattr *attr)
++{
++
++ struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb);
++ struct inode *inode = dentry->d_inode;
++ unsigned int ia_valid;
++ int error;
++ loff_t old_size;
++
++ PRINTK("exfat_setattr entered\n");
++
++ if ((attr->ia_valid & ATTR_SIZE)
++ && (attr->ia_size > i_size_read(inode))) {
++ error = exfat_cont_expand(inode, attr->ia_size);
++ if (error || attr->ia_valid == ATTR_SIZE)
++ return error;
++ attr->ia_valid &= ~ATTR_SIZE;
++ }
++
++ ia_valid = attr->ia_valid;
++
++ if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET))
++ && exfat_allow_set_time(sbi, inode)) {
++ attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET);
++ }
++
++ error = inode_change_ok(inode, attr);
++ attr->ia_valid = ia_valid;
++ if (error) {
++ return error;
++ }
++
++ if (((attr->ia_valid & ATTR_UID) &&
++ (attr->ia_uid != sbi->options.fs_uid)) ||
++ ((attr->ia_valid & ATTR_GID) &&
++ (attr->ia_gid != sbi->options.fs_gid)) ||
++ ((attr->ia_valid & ATTR_MODE) &&
++ (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) {
++ return -EPERM;
++ }
++
++ if (attr->ia_valid & ATTR_MODE) {
++ if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
++ attr->ia_valid &= ~ATTR_MODE;
++ }
++
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++ if (attr->ia_valid)
++ error = inode_setattr(inode, attr);
++#else
++ if (attr->ia_valid & ATTR_SIZE) {
++ old_size = i_size_read(inode);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ down_write(&EXFAT_I(inode)->truncate_lock);
++ truncate_setsize(inode, attr->ia_size);
++ _exfat_truncate(inode, old_size);
++ up_write(&EXFAT_I(inode)->truncate_lock);
++#else
++ truncate_setsize(inode, attr->ia_size);
++ _exfat_truncate(inode, old_size);
++#endif
++ }
++ setattr_copy(inode, attr);
++ mark_inode_dirty(inode);
++#endif
++
++
++ PRINTK("exfat_setattr exited\n");
++ return error;
++}
++
++static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
++{
++ struct inode *inode = dentry->d_inode;
++
++ PRINTK("exfat_getattr entered\n");
++
++ generic_fillattr(inode, stat);
++ stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size;
++
++ PRINTK("exfat_getattr exited\n");
++ return 0;
++}
++
++const struct inode_operations exfat_dir_inode_operations = {
++ .create = exfat_create,
++ .lookup = exfat_lookup,
++ .unlink = exfat_unlink,
++ .symlink = exfat_symlink,
++ .mkdir = exfat_mkdir,
++ .rmdir = exfat_rmdir,
++ .rename = exfat_rename,
++ .setattr = exfat_setattr,
++ .getattr = exfat_getattr,
++};
++
++static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++ struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode);
++ nd_set_link(nd, (char *)(ei->target));
++ return NULL;
++}
++
++const struct inode_operations exfat_symlink_inode_operations = {
++ .readlink = generic_readlink,
++ .follow_link = exfat_follow_link,
++};
++
++static int exfat_file_release(struct inode *inode, struct file *filp)
++{
++ struct super_block *sb = inode->i_sb;
++
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++ FsSyncVol(sb, 0);
++ return 0;
++}
++
++const struct file_operations exfat_file_operations = {
++ .llseek = generic_file_llseek,
++ .read = do_sync_read,
++ .write = do_sync_write,
++ .aio_read = generic_file_aio_read,
++ .aio_write = generic_file_aio_write,
++ .mmap = generic_file_mmap,
++ .release = exfat_file_release,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++ .ioctl = exfat_generic_ioctl,
++ .fsync = exfat_file_fsync,
++#else
++ .unlocked_ioctl = exfat_generic_ioctl,
++ .fsync = generic_file_fsync,
++#endif
++ .splice_read = generic_file_splice_read,
++};
++
++static void _exfat_truncate(struct inode *inode, loff_t old_size)
++{
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++ int err;
++
++ __lock_super(sb);
++
++ if (EXFAT_I(inode)->mmu_private > i_size_read(inode))
++ EXFAT_I(inode)->mmu_private = i_size_read(inode);
++
++ if (EXFAT_I(inode)->fid.start_clu == 0) goto out;
++
++ err = FsTruncateFile(inode, old_size, i_size_read(inode));
++ if (err) goto out;
++
++ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
++ if (IS_DIRSYNC(inode))
++ (void) exfat_sync_inode(inode);
++ else
++ mark_inode_dirty(inode);
++
++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1))
++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9;
++out:
++ __unlock_super(sb);
++}
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)
++static void exfat_truncate(struct inode *inode)
++{
++ _exfat_truncate(inode, i_size_read(inode));
++}
++#endif
++
++const struct inode_operations exfat_file_inode_operations = {
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36)
++ .truncate = exfat_truncate,
++#endif
++ .setattr = exfat_setattr,
++ .getattr = exfat_getattr,
++};
++
++static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
++ unsigned long *mapped_blocks, int *create)
++{
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++ BD_INFO_T *p_bd = &(sbi->bd_info);
++ const unsigned long blocksize = sb->s_blocksize;
++ const unsigned char blocksize_bits = sb->s_blocksize_bits;
++ sector_t last_block;
++ int err, clu_offset, sec_offset;
++ unsigned int cluster;
++
++ *phys = 0;
++ *mapped_blocks = 0;
++
++ if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) {
++ if (inode->i_ino == EXFAT_ROOT_INO) {
++ if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) {
++ *phys = sector + p_fs->root_start_sector;
++ *mapped_blocks = 1;
++ }
++ return 0;
++ }
++ }
++
++ last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits;
++ if (sector >= last_block) {
++ if (*create == 0) return 0;
++ } else {
++ *create = 0;
++ }
++
++ clu_offset = sector >> p_fs->sectors_per_clu_bits;
++ sec_offset = sector & (p_fs->sectors_per_clu - 1);
++
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++
++ err = FsMapCluster(inode, clu_offset, &cluster);
++
++ if (err) {
++ if (err == FFS_FULL)
++ return -ENOSPC;
++ else
++ return -EIO;
++ } else if (cluster != CLUSTER_32(~0)) {
++ *phys = START_SECTOR(cluster) + sec_offset;
++ *mapped_blocks = p_fs->sectors_per_clu - sec_offset;
++ }
++
++ return 0;
++}
++
++static int exfat_get_block(struct inode *inode, sector_t iblock,
++ struct buffer_head *bh_result, int create)
++{
++ struct super_block *sb = inode->i_sb;
++ unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
++ int err;
++ unsigned long mapped_blocks;
++ sector_t phys;
++
++ __lock_super(sb);
++
++ err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create);
++ if (err) {
++ __unlock_super(sb);
++ return err;
++ }
++
++ if (phys) {
++ max_blocks = min(mapped_blocks, max_blocks);
++ if (create) {
++ EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits;
++ set_buffer_new(bh_result);
++ }
++ map_bh(bh_result, sb, phys);
++ }
++
++ bh_result->b_size = max_blocks << sb->s_blocksize_bits;
++ __unlock_super(sb);
++
++ return 0;
++}
++
++static int exfat_readpage(struct file *file, struct page *page)
++{
++ int ret;
++ ret = mpage_readpage(page, exfat_get_block);
++ return ret;
++}
++
++static int exfat_readpages(struct file *file, struct address_space *mapping,
++ struct list_head *pages, unsigned nr_pages)
++{
++ int ret;
++ ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block);
++ return ret;
++}
++
++static int exfat_writepage(struct page *page, struct writeback_control *wbc)
++{
++ int ret;
++ ret = block_write_full_page(page, exfat_get_block, wbc);
++ return ret;
++}
++
++static int exfat_writepages(struct address_space *mapping,
++ struct writeback_control *wbc)
++{
++ int ret;
++ ret = mpage_writepages(mapping, wbc, exfat_get_block);
++ return ret;
++}
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
++static void exfat_write_failed(struct address_space *mapping, loff_t to)
++{
++ struct inode *inode = mapping->host;
++ if (to > i_size_read(inode)) {
++ truncate_pagecache(inode, to, i_size_read(inode));
++ EXFAT_I(inode)->fid.size = i_size_read(inode);
++ _exfat_truncate(inode, i_size_read(inode));
++ }
++}
++#endif
++
++
++static int exfat_write_begin(struct file *file, struct address_space *mapping,
++ loff_t pos, unsigned len, unsigned flags,
++ struct page **pagep, void **fsdata)
++{
++ int ret;
++ *pagep = NULL;
++ ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
++ exfat_get_block,
++ &EXFAT_I(mapping->host)->mmu_private);
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
++ if (ret < 0)
++ exfat_write_failed(mapping, pos+len);
++#endif
++ return ret;
++}
++
++static int exfat_write_end(struct file *file, struct address_space *mapping,
++ loff_t pos, unsigned len, unsigned copied,
++ struct page *pagep, void *fsdata)
++{
++ struct inode *inode = mapping->host;
++ FILE_ID_T *fid = &(EXFAT_I(inode)->fid);
++ int err;
++
++ err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
++ if (err < len)
++ exfat_write_failed(mapping, pos+len);
++#endif
++
++ if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) {
++ inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
++ fid->attr |= ATTR_ARCHIVE;
++ mark_inode_dirty(inode);
++ }
++ return err;
++}
++
++static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb,
++ const struct iovec *iov,
++ loff_t offset, unsigned long nr_segs)
++{
++ struct inode *inode = iocb->ki_filp->f_mapping->host;
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
++ struct address_space *mapping = iocb->ki_filp->f_mapping;
++#endif
++ ssize_t ret;
++
++ if (rw == WRITE) {
++ if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs)))
++ return 0;
++ }
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ ret = blockdev_direct_IO(rw, iocb, inode, iov,
++ offset, nr_segs, exfat_get_block);
++#else
++ ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
++ offset, nr_segs, exfat_get_block, NULL);
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
++ if ((ret < 0) && (rw & WRITE))
++ exfat_write_failed(mapping, offset+iov_length(iov, nr_segs));
++#endif
++ return ret;
++
++}
++
++static sector_t _exfat_bmap(struct address_space *mapping, sector_t block)
++{
++ sector_t blocknr;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ down_read(&EXFAT_I(mapping->host)->truncate_lock);
++ blocknr = generic_block_bmap(mapping, block, exfat_get_block);
++ up_read(&EXFAT_I(mapping->host)->truncate_lock);
++#else
++ down_read(&mapping->host->i_alloc_sem);
++ blocknr = generic_block_bmap(mapping, block, exfat_get_block);
++ up_read(&mapping->host->i_alloc_sem);
++#endif
++
++ return blocknr;
++}
++
++const struct address_space_operations exfat_aops = {
++ .readpage = exfat_readpage,
++ .readpages = exfat_readpages,
++ .writepage = exfat_writepage,
++ .writepages = exfat_writepages,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
++ .sync_page = block_sync_page,
++#endif
++ .write_begin = exfat_write_begin,
++ .write_end = exfat_write_end,
++ .direct_IO = exfat_direct_IO,
++ .bmap = _exfat_bmap
++};
++
++static inline unsigned long exfat_hash(loff_t i_pos)
++{
++ return hash_32(i_pos, EXFAT_HASH_BITS);
++}
++
++static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) {
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ struct exfat_inode_info *info;
++ struct hlist_node *node;
++ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
++ struct inode *inode = NULL;
++
++ spin_lock(&sbi->inode_hash_lock);
++ hlist_for_each_entry(info, node, head, i_hash_fat) {
++ CHECK_ERR(info->vfs_inode.i_sb != sb);
++
++ if (i_pos != info->i_pos)
++ continue;
++ inode = igrab(&info->vfs_inode);
++ if (inode)
++ break;
++ }
++ spin_unlock(&sbi->inode_hash_lock);
++ return inode;
++}
++
++static void exfat_attach(struct inode *inode, loff_t i_pos)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
++ struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos);
++
++ spin_lock(&sbi->inode_hash_lock);
++ EXFAT_I(inode)->i_pos = i_pos;
++ hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head);
++ spin_unlock(&sbi->inode_hash_lock);
++}
++
++static void exfat_detach(struct inode *inode)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
++
++ spin_lock(&sbi->inode_hash_lock);
++ hlist_del_init(&EXFAT_I(inode)->i_hash_fat);
++ EXFAT_I(inode)->i_pos = 0;
++ spin_unlock(&sbi->inode_hash_lock);
++}
++
++static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++ DIR_ENTRY_T info;
++
++ memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T));
++
++ FsReadStat(inode, &info);
++
++ EXFAT_I(inode)->i_pos = 0;
++ EXFAT_I(inode)->target = NULL;
++ inode->i_uid = sbi->options.fs_uid;
++ inode->i_gid = sbi->options.fs_gid;
++ inode->i_version++;
++ inode->i_generation = get_seconds();
++
++ if (info.Attr & ATTR_SUBDIR) {
++ inode->i_generation &= ~1;
++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
++ inode->i_op = &exfat_dir_inode_operations;
++ inode->i_fop = &exfat_dir_operations;
++
++ i_size_write(inode, info.Size);
++ EXFAT_I(inode)->mmu_private = i_size_read(inode);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ set_nlink(inode,info.NumSubdirs);
++#else
++ inode->i_nlink = info.NumSubdirs;
++#endif
++ } else if (info.Attr & ATTR_SYMLINK) {
++ inode->i_generation |= 1;
++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
++ inode->i_op = &exfat_symlink_inode_operations;
++
++ i_size_write(inode, info.Size);
++ EXFAT_I(inode)->mmu_private = i_size_read(inode);
++ } else {
++ inode->i_generation |= 1;
++ inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO);
++ inode->i_op = &exfat_file_inode_operations;
++ inode->i_fop = &exfat_file_operations;
++ inode->i_mapping->a_ops = &exfat_aops;
++ inode->i_mapping->nrpages = 0;
++
++ i_size_write(inode, info.Size);
++ EXFAT_I(inode)->mmu_private = i_size_read(inode);
++ }
++ exfat_save_attr(inode, info.Attr);
++
++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1))
++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9;
++
++ exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp);
++ exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp);
++ exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp);
++
++ return 0;
++}
++
++static struct inode *exfat_build_inode(struct super_block *sb,
++ FILE_ID_T *fid, loff_t i_pos) {
++ struct inode *inode;
++ int err;
++
++ inode = exfat_iget(sb, i_pos);
++ if (inode)
++ goto out;
++ inode = new_inode(sb);
++ if (!inode) {
++ inode = ERR_PTR(-ENOMEM);
++ goto out;
++ }
++ inode->i_ino = iunique(sb, EXFAT_ROOT_INO);
++ inode->i_version = 1;
++ err = exfat_fill_inode(inode, fid);
++ if (err) {
++ iput(inode);
++ inode = ERR_PTR(err);
++ goto out;
++ }
++ exfat_attach(inode, i_pos);
++ insert_inode_hash(inode);
++out:
++ return inode;
++}
++
++static int exfat_sync_inode(struct inode *inode)
++{
++ return exfat_write_inode(inode, NULL);
++}
++
++static struct inode *exfat_alloc_inode(struct super_block *sb) {
++ struct exfat_inode_info *ei;
++
++ ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS);
++ if (!ei)
++ return NULL;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ init_rwsem(&ei->truncate_lock);
++#endif
++
++ return &ei->vfs_inode;
++}
++
++static void exfat_destroy_inode(struct inode *inode)
++{
++ FREE(EXFAT_I(inode)->target);
++ EXFAT_I(inode)->target = NULL;
++
++ kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode));
++}
++
++static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
++{
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ DIR_ENTRY_T info;
++
++ if (inode->i_ino == EXFAT_ROOT_INO)
++ return 0;
++
++ info.Attr = exfat_make_attr(inode);
++ info.Size = i_size_read(inode);
++
++ exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp);
++ exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp);
++ exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp);
++
++ FsWriteStat(inode, &info);
++
++ return 0;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++static void exfat_delete_inode(struct inode *inode)
++{
++ truncate_inode_pages(&inode->i_data, 0);
++ clear_inode(inode);
++}
++
++static void exfat_clear_inode(struct inode *inode)
++{
++ exfat_detach(inode);
++ remove_inode_hash(inode);
++}
++#else
++static void exfat_evict_inode(struct inode *inode)
++{
++ truncate_inode_pages(&inode->i_data, 0);
++
++ if (!inode->i_nlink)
++ i_size_write(inode, 0);
++ invalidate_inode_buffers(inode);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00)
++ end_writeback(inode);
++#else
++ clear_inode(inode);
++#endif
++ exfat_detach(inode);
++
++ remove_inode_hash(inode);
++}
++#endif
++
++
++static void exfat_put_super(struct super_block *sb)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ if (__is_sb_dirty(sb))
++ exfat_write_super(sb);
++
++ FsUmountVol(sb);
++
++ if (sbi->nls_disk) {
++ unload_nls(sbi->nls_disk);
++ sbi->nls_disk = NULL;
++ sbi->options.codepage = exfat_default_codepage;
++ }
++ if (sbi->nls_io) {
++ unload_nls(sbi->nls_io);
++ sbi->nls_io = NULL;
++ }
++ if (sbi->options.iocharset != exfat_default_iocharset) {
++ kfree(sbi->options.iocharset);
++ sbi->options.iocharset = exfat_default_iocharset;
++ }
++
++ sb->s_fs_info = NULL;
++ kfree(sbi);
++}
++
++static void exfat_write_super(struct super_block *sb)
++{
++ __lock_super(sb);
++
++ __set_sb_clean(sb);
++
++ if (!(sb->s_flags & MS_RDONLY))
++ FsSyncVol(sb, 1);
++
++ __unlock_super(sb);
++}
++
++static int exfat_sync_fs(struct super_block *sb, int wait)
++{
++ int err = 0;
++
++ if (__is_sb_dirty(sb)) {
++ __lock_super(sb);
++ __set_sb_clean(sb);
++ err = FsSyncVol(sb, 1);
++ __unlock_super(sb);
++ }
++
++ return err;
++}
++
++static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
++{
++ struct super_block *sb = dentry->d_sb;
++ u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
++ FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info);
++ VOL_INFO_T info;
++
++ if (p_fs->used_clusters == (UINT32) ~0) {
++ if (FFS_MEDIAERR == FsGetVolInfo(sb, &info))
++ return -EIO;
++
++ } else {
++ info.FatType = p_fs->vol_type;
++ info.ClusterSize = p_fs->cluster_size;
++ info.NumClusters = p_fs->num_clusters - 2;
++ info.UsedClusters = p_fs->used_clusters;
++ info.FreeClusters = info.NumClusters - info.UsedClusters;
++
++ if (p_fs->dev_ejected)
++ return -EIO;
++ }
++
++ buf->f_type = sb->s_magic;
++ buf->f_bsize = info.ClusterSize;
++ buf->f_blocks = info.NumClusters;
++ buf->f_bfree = info.FreeClusters;
++ buf->f_bavail = info.FreeClusters;
++ buf->f_fsid.val[0] = (u32)id;
++ buf->f_fsid.val[1] = (u32)(id >> 32);
++ buf->f_namelen = 260;
++
++ return 0;
++}
++
++static int exfat_remount(struct super_block *sb, int *flags, char *data)
++{
++ *flags |= MS_NODIRATIME;
++ return 0;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++static int exfat_show_options(struct seq_file *m, struct dentry *root)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb);
++#else
++static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb);
++#endif
++ struct exfat_mount_options *opts = &sbi->options;
++
++ if (opts->fs_uid != 0)
++ seq_printf(m, ",uid=%u", opts->fs_uid);
++ if (opts->fs_gid != 0)
++ seq_printf(m, ",gid=%u", opts->fs_gid);
++ seq_printf(m, ",fmask=%04o", opts->fs_fmask);
++ seq_printf(m, ",dmask=%04o", opts->fs_dmask);
++ if (opts->allow_utime)
++ seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
++ if (sbi->nls_disk)
++ seq_printf(m, ",codepage=%s", sbi->nls_disk->charset);
++ if (sbi->nls_io)
++ seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
++ seq_printf(m, ",namecase=%u", opts->casesensitive);
++ if (opts->errors == EXFAT_ERRORS_CONT)
++ seq_puts(m, ",errors=continue");
++ else if (opts->errors == EXFAT_ERRORS_PANIC)
++ seq_puts(m, ",errors=panic");
++ else
++ seq_puts(m, ",errors=remount-ro");
++#if EXFAT_CONFIG_DISCARD
++ if (opts->discard)
++ seq_printf(m, ",discard");
++#endif
++ return 0;
++}
++
++const struct super_operations exfat_sops = {
++ .alloc_inode = exfat_alloc_inode,
++ .destroy_inode = exfat_destroy_inode,
++ .write_inode = exfat_write_inode,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
++ .delete_inode = exfat_delete_inode,
++ .clear_inode = exfat_clear_inode,
++#else
++ .evict_inode = exfat_evict_inode,
++#endif
++ .put_super = exfat_put_super,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00)
++ .write_super = exfat_write_super,
++#endif
++ .sync_fs = exfat_sync_fs,
++ .statfs = exfat_statfs,
++ .remount_fs = exfat_remount,
++ .show_options = exfat_show_options,
++};
++
++enum {
++ Opt_uid,
++ Opt_gid,
++ Opt_umask,
++ Opt_dmask,
++ Opt_fmask,
++ Opt_allow_utime,
++ Opt_codepage,
++ Opt_charset,
++ Opt_namecase,
++ Opt_debug,
++ Opt_err_cont,
++ Opt_err_panic,
++ Opt_err_ro,
++ Opt_err,
++#if EXFAT_CONFIG_DISCARD
++ Opt_discard,
++#endif
++};
++
++static const match_table_t exfat_tokens = {
++ {Opt_uid, "uid=%u"},
++ {Opt_gid, "gid=%u"},
++ {Opt_umask, "umask=%o"},
++ {Opt_dmask, "dmask=%o"},
++ {Opt_fmask, "fmask=%o"},
++ {Opt_allow_utime, "allow_utime=%o"},
++ {Opt_codepage, "codepage=%u"},
++ {Opt_charset, "iocharset=%s"},
++ {Opt_namecase, "namecase=%u"},
++ {Opt_debug, "debug"},
++ {Opt_err_cont, "errors=continue"},
++ {Opt_err_panic, "errors=panic"},
++ {Opt_err_ro, "errors=remount-ro"},
++#if EXFAT_CONFIG_DISCARD
++ {Opt_discard, "discard"},
++#endif
++ {Opt_err, NULL}
++};
++
++static int parse_options(char *options, int silent, int *debug,
++ struct exfat_mount_options *opts)
++{
++ char *p;
++ substring_t args[MAX_OPT_ARGS];
++ int option;
++ char *iocharset;
++
++ opts->fs_uid = current_uid();
++ opts->fs_gid = current_gid();
++ opts->fs_fmask = opts->fs_dmask = current->fs->umask;
++ opts->allow_utime = (unsigned short) -1;
++ opts->codepage = exfat_default_codepage;
++ opts->iocharset = exfat_default_iocharset;
++ opts->casesensitive = 0;
++ opts->errors = EXFAT_ERRORS_RO;
++#if EXFAT_CONFIG_DISCARD
++ opts->discard = 0;
++#endif
++ *debug = 0;
++
++ if (!options)
++ goto out;
++
++ while ((p = strsep(&options, ",")) != NULL) {
++ int token;
++ if (!*p)
++ continue;
++
++ token = match_token(p, exfat_tokens, args);
++ switch (token) {
++ case Opt_uid:
++ if (match_int(&args[0], &option))
++ return 0;
++ opts->fs_uid = option;
++ break;
++ case Opt_gid:
++ if (match_int(&args[0], &option))
++ return 0;
++ opts->fs_gid = option;
++ break;
++ case Opt_umask:
++ case Opt_dmask:
++ case Opt_fmask:
++ if (match_octal(&args[0], &option))
++ return 0;
++ if (token != Opt_dmask)
++ opts->fs_fmask = option;
++ if (token != Opt_fmask)
++ opts->fs_dmask = option;
++ break;
++ case Opt_allow_utime:
++ if (match_octal(&args[0], &option))
++ return 0;
++ opts->allow_utime = option & (S_IWGRP | S_IWOTH);
++ break;
++ case Opt_codepage:
++ if (match_int(&args[0], &option))
++ return 0;
++ opts->codepage = option;
++ break;
++ case Opt_charset:
++ if (opts->iocharset != exfat_default_iocharset)
++ kfree(opts->iocharset);
++ iocharset = match_strdup(&args[0]);
++ if (!iocharset)
++ return -ENOMEM;
++ opts->iocharset = iocharset;
++ break;
++ case Opt_namecase:
++ if (match_int(&args[0], &option))
++ return 0;
++ opts->casesensitive = option;
++ break;
++ case Opt_err_cont:
++ opts->errors = EXFAT_ERRORS_CONT;
++ break;
++ case Opt_err_panic:
++ opts->errors = EXFAT_ERRORS_PANIC;
++ break;
++ case Opt_err_ro:
++ opts->errors = EXFAT_ERRORS_RO;
++ break;
++ case Opt_debug:
++ *debug = 1;
++ break;
++#if EXFAT_CONFIG_DISCARD
++ case Opt_discard:
++ opts->discard = 1;
++ break;
++#endif
++ default:
++ if (!silent) {
++ printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p);
++ }
++ return -EINVAL;
++ }
++ }
++
++out:
++ if (opts->allow_utime == (unsigned short) -1)
++ opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH);
++
++ return 0;
++}
++
++static void exfat_hash_init(struct super_block *sb)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ int i;
++
++ spin_lock_init(&sbi->inode_hash_lock);
++ for (i = 0; i < EXFAT_HASH_SIZE; i++)
++ INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
++}
++
++static int exfat_read_root(struct inode *inode)
++{
++ struct super_block *sb = inode->i_sb;
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ struct timespec ts;
++ FS_INFO_T *p_fs = &(sbi->fs_info);
++ DIR_ENTRY_T info;
++
++ ts = CURRENT_TIME_SEC;
++
++ EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir;
++ EXFAT_I(inode)->fid.dir.flags = 0x01;
++ EXFAT_I(inode)->fid.entry = -1;
++ EXFAT_I(inode)->fid.start_clu = p_fs->root_dir;
++ EXFAT_I(inode)->fid.flags = 0x01;
++ EXFAT_I(inode)->fid.type = TYPE_DIR;
++ EXFAT_I(inode)->fid.rwoffset = 0;
++ EXFAT_I(inode)->fid.hint_last_off = -1;
++
++ EXFAT_I(inode)->target = NULL;
++
++ FsReadStat(inode, &info);
++
++ inode->i_uid = sbi->options.fs_uid;
++ inode->i_gid = sbi->options.fs_gid;
++ inode->i_version++;
++ inode->i_generation = 0;
++ inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO);
++ inode->i_op = &exfat_dir_inode_operations;
++ inode->i_fop = &exfat_dir_operations;
++
++ i_size_write(inode, info.Size);
++ inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1))
++ & ~((loff_t)p_fs->cluster_size - 1)) >> 9;
++ EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff;
++ EXFAT_I(inode)->mmu_private = i_size_read(inode);
++
++ exfat_save_attr(inode, ATTR_SUBDIR);
++ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ set_nlink(inode,info.NumSubdirs + 2);
++#else
++ inode->i_nlink = info.NumSubdirs + 2;
++#endif
++
++ return 0;
++}
++
++static int exfat_fill_super(struct super_block *sb, void *data, int silent)
++{
++ struct inode *root_inode = NULL;
++ struct exfat_sb_info *sbi;
++ int debug, ret;
++ long error;
++ char buf[50];
++
++ sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
++ if (!sbi)
++ return -ENOMEM;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
++ mutex_init(&sbi->s_lock);
++#endif
++ sb->s_fs_info = sbi;
++
++ sb->s_flags |= MS_NODIRATIME;
++ sb->s_magic = EXFAT_SUPER_MAGIC;
++ sb->s_op = &exfat_sops;
++
++ error = parse_options(data, silent, &debug, &sbi->options);
++ if (error)
++ goto out_fail;
++
++ error = -EIO;
++ sb_min_blocksize(sb, 512);
++ sb->s_maxbytes = 0x7fffffffffffffffLL;
++
++ ret = FsMountVol(sb);
++ if (ret) {
++ if (!silent)
++ printk(KERN_ERR "[EXFAT] FsMountVol failed\n");
++
++ goto out_fail;
++ }
++
++ exfat_hash_init(sb);
++
++ error = -EINVAL;
++ sprintf(buf, "cp%d", sbi->options.codepage);
++ sbi->nls_disk = load_nls(buf);
++ if (!sbi->nls_disk) {
++ printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf);
++ goto out_fail2;
++ }
++
++ sbi->nls_io = load_nls(sbi->options.iocharset);
++ if (!sbi->nls_io) {
++ printk(KERN_ERR "[EXFAT] IO charset %s not found\n",
++ sbi->options.iocharset);
++ goto out_fail2;
++ }
++
++ error = -ENOMEM;
++ root_inode = new_inode(sb);
++ if (!root_inode)
++ goto out_fail2;
++ root_inode->i_ino = EXFAT_ROOT_INO;
++ root_inode->i_version = 1;
++ error = exfat_read_root(root_inode);
++ if (error < 0)
++ goto out_fail2;
++ error = -ENOMEM;
++ exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos);
++ insert_inode_hash(root_inode);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ sb->s_root = d_make_root(root_inode);
++#else
++ sb->s_root = d_alloc_root(root_inode);
++#endif
++ if (!sb->s_root) {
++ printk(KERN_ERR "[EXFAT] Getting the root inode failed\n");
++ goto out_fail2;
++ }
++
++ return 0;
++
++out_fail2:
++ FsUmountVol(sb);
++out_fail:
++ if (root_inode)
++ iput(root_inode);
++ if (sbi->nls_io)
++ unload_nls(sbi->nls_io);
++ if (sbi->nls_disk)
++ unload_nls(sbi->nls_disk);
++ if (sbi->options.iocharset != exfat_default_iocharset)
++ kfree(sbi->options.iocharset);
++ sb->s_fs_info = NULL;
++ kfree(sbi);
++ return error;
++}
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
++static int exfat_get_sb(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data, struct vfsmount *mnt)
++{
++ return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt);
++}
++#else
++static struct dentry *exfat_fs_mount(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data) {
++ return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super);
++}
++#endif
++
++static void init_once(void *foo)
++{
++ struct exfat_inode_info *ei = (struct exfat_inode_info *)foo;
++
++ INIT_HLIST_NODE(&ei->i_hash_fat);
++ inode_init_once(&ei->vfs_inode);
++}
++
++static int __init exfat_init_inodecache(void)
++{
++ exfat_inode_cachep = kmem_cache_create("exfat_inode_cache",
++ sizeof(struct exfat_inode_info),
++ 0, (SLAB_RECLAIM_ACCOUNT|
++ SLAB_MEM_SPREAD),
++ init_once);
++ if (exfat_inode_cachep == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++static void __exit exfat_destroy_inodecache(void)
++{
++ kmem_cache_destroy(exfat_inode_cachep);
++}
++
++#if EXFAT_CONFIG_KERNEL_DEBUG
++static void exfat_debug_kill_sb(struct super_block *sb)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(sb);
++ struct block_device *bdev = sb->s_bdev;
++
++ long flags;
++
++ if (sbi) {
++ flags = sbi->debug_flags;
++
++ if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) {
++ FsReleaseCache(sb);
++ invalidate_bdev(bdev);
++ }
++ }
++
++ kill_block_super(sb);
++}
++#endif
++
++static struct file_system_type exfat_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "exfat",
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
++ .get_sb = exfat_get_sb,
++#else
++ .mount = exfat_fs_mount,
++#endif
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ .kill_sb = exfat_debug_kill_sb,
++#else
++ .kill_sb = kill_block_super,
++#endif
++ .fs_flags = FS_REQUIRES_DEV,
++};
++
++static int __init init_exfat_fs(void)
++{
++ int err;
++
++ printk(KERN_INFO "exFAT: FS Version %s\n", EXFAT_VERSION);
++
++ err = exfat_init_inodecache();
++ if (err) return err;
++
++ return register_filesystem(&exfat_fs_type);
++}
++
++static void __exit exit_exfat_fs(void)
++{
++ exfat_destroy_inodecache();
++ unregister_filesystem(&exfat_fs_type);
++}
++
++module_init(init_exfat_fs);
++module_exit(exit_exfat_fs);
++
++MODULE_LICENSE("GPL");
+diff --git a/drivers/staging/exfat/exfat_super.h b/drivers/staging/exfat/exfat_super.h
+new file mode 100644
+index 00000000..22f4ee26
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_super.h
+@@ -0,0 +1,149 @@
++/* Some of the source code in this file came from "linux/fs/fat/fat.h". */
++
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#ifndef _EXFAT_LINUX_H
++#define _EXFAT_LINUX_H
++
++#include <linux/buffer_head.h>
++#include <linux/string.h>
++#include <linux/nls.h>
++#include <linux/fs.h>
++#include <linux/mutex.h>
++#include <linux/swap.h>
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++#include "exfat_data.h"
++#include "exfat_oal.h"
++
++#include "exfat_blkdev.h"
++#include "exfat_cache.h"
++#include "exfat_part.h"
++#include "exfat_nls.h"
++#include "exfat_api.h"
++#include "exfat.h"
++
++#define EXFAT_ERRORS_CONT 1
++#define EXFAT_ERRORS_PANIC 2
++#define EXFAT_ERRORS_RO 3
++
++#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32)
++
++struct exfat_mount_options {
++ uid_t fs_uid;
++ gid_t fs_gid;
++ unsigned short fs_fmask;
++ unsigned short fs_dmask;
++ unsigned short allow_utime;
++ unsigned short codepage;
++ char *iocharset;
++ unsigned char casesensitive;
++ unsigned char errors;
++#if EXFAT_CONFIG_DISCARD
++ unsigned char discard;
++#endif
++};
++
++#define EXFAT_HASH_BITS 8
++#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS)
++
++struct exfat_sb_info {
++ FS_INFO_T fs_info;
++ BD_INFO_T bd_info;
++
++ struct exfat_mount_options options;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00)
++ int s_dirt;
++ struct mutex s_lock;
++#endif
++ struct nls_table *nls_disk;
++ struct nls_table *nls_io;
++
++ struct inode *fat_inode;
++
++ spinlock_t inode_hash_lock;
++ struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
++#if EXFAT_CONFIG_KERNEL_DEBUG
++ long debug_flags;
++#endif
++};
++
++struct exfat_inode_info {
++ FILE_ID_T fid;
++ char *target;
++ loff_t mmu_private;
++ loff_t i_pos;
++ struct hlist_node i_hash_fat;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00)
++ struct rw_semaphore truncate_lock;
++#endif
++ struct inode vfs_inode;
++};
++
++#define EXFAT_SB(sb) ((struct exfat_sb_info *)((sb)->s_fs_info))
++
++static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) {
++ return container_of(inode, struct exfat_inode_info, vfs_inode);
++}
++
++static inline int exfat_mode_can_hold_ro(struct inode *inode)
++{
++ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
++
++ if (S_ISDIR(inode->i_mode))
++ return 0;
++
++ if ((~sbi->options.fs_fmask) & S_IWUGO)
++ return 1;
++ return 0;
++}
++
++static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
++ u32 attr, mode_t mode)
++{
++ if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
++ mode &= ~S_IWUGO;
++
++ if (attr & ATTR_SUBDIR)
++ return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
++ else if (attr & ATTR_SYMLINK)
++ return (mode & ~sbi->options.fs_dmask) | S_IFLNK;
++ else
++ return (mode & ~sbi->options.fs_fmask) | S_IFREG;
++}
++
++static inline u32 exfat_make_attr(struct inode *inode)
++{
++ if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO))
++ return ((EXFAT_I(inode)->fid.attr) | ATTR_READONLY);
++ else
++ return (EXFAT_I(inode)->fid.attr);
++}
++
++static inline void exfat_save_attr(struct inode *inode, u32 attr)
++{
++ if (exfat_mode_can_hold_ro(inode))
++ EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK;
++ else
++ EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY);
++}
++
++#endif
+diff --git a/drivers/staging/exfat/exfat_upcase.c b/drivers/staging/exfat/exfat_upcase.c
+new file mode 100644
+index 00000000..7378a157
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_upcase.c
+@@ -0,0 +1,390 @@
++/*
++ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "exfat_config.h"
++#include "exfat_global.h"
++
++#include "exfat_nls.h"
++
++UINT8 uni_upcase[NUM_UPCASE<<1] = {
++ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
++ 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00,
++ 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
++ 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00,
++ 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
++ 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00,
++ 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
++ 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00,
++ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
++ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00,
++ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
++ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00,
++ 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
++ 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00,
++ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
++ 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00,
++ 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
++ 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00,
++ 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
++ 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00,
++ 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00,
++ 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00,
++ 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00,
++ 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00,
++ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00,
++ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00,
++ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00,
++ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00,
++ 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00,
++ 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00,
++ 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00,
++ 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01,
++ 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
++ 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01,
++ 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
++ 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01,
++ 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
++ 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01,
++ 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
++ 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01,
++ 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
++ 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01,
++ 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
++ 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01,
++ 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
++ 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01,
++ 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
++ 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01,
++ 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
++ 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01,
++ 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01,
++ 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01,
++ 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01,
++ 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01,
++ 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01,
++ 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01,
++ 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01,
++ 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01,
++ 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01,
++ 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01,
++ 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01,
++ 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01,
++ 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01,
++ 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01,
++ 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
++ 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02,
++ 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
++ 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02,
++ 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
++ 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02,
++ 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
++ 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02,
++ 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
++ 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02,
++ 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01,
++ 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02,
++ 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
++ 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01,
++ 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02,
++ 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02,
++ 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
++ 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02,
++ 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
++ 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02,
++ 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02,
++ 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02,
++ 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02,
++ 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02,
++ 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02,
++ 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02,
++ 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02,
++ 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02,
++ 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02,
++ 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02,
++ 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02,
++ 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02,
++ 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
++ 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03,
++ 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
++ 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03,
++ 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
++ 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03,
++ 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
++ 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03,
++ 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
++ 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03,
++ 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
++ 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03,
++ 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
++ 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03,
++ 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
++ 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03,
++ 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
++ 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03,
++ 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
++ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03,
++ 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03,
++ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03,
++ 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
++ 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03,
++ 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03,
++ 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03,
++ 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03,
++ 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03,
++ 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03,
++ 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03,
++ 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03,
++ 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03,
++ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
++ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04,
++ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
++ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04,
++ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
++ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04,
++ 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
++ 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04,
++ 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
++ 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04,
++ 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
++ 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04,
++ 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
++ 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04,
++ 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
++ 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04,
++ 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
++ 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04,
++ 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
++ 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04,
++ 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04,
++ 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04,
++ 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04,
++ 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04,
++ 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04,
++ 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04,
++ 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04,
++ 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04,
++ 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04,
++ 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04,
++ 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04,
++ 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04,
++ 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
++ 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05,
++ 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
++ 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05,
++ 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
++ 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05,
++ 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
++ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05,
++ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
++ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05,
++ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
++ 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05,
++ 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
++ 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05,
++ 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
++ 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05,
++ 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF,
++ 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D,
++ 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D,
++ 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D,
++ 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D,
++ 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D,
++ 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D,
++ 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D,
++ 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D,
++ 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D,
++ 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D,
++ 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D,
++ 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D,
++ 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D,
++ 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D,
++ 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D,
++ 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D,
++ 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E,
++ 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E,
++ 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E,
++ 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E,
++ 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E,
++ 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E,
++ 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E,
++ 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E,
++ 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E,
++ 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E,
++ 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E,
++ 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E,
++ 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E,
++ 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E,
++ 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E,
++ 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E,
++ 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E,
++ 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E,
++ 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E,
++ 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E,
++ 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E,
++ 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E,
++ 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E,
++ 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E,
++ 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E,
++ 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E,
++ 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E,
++ 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E,
++ 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E,
++ 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E,
++ 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E,
++ 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E,
++ 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F,
++ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F,
++ 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F,
++ 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F,
++ 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F,
++ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F,
++ 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F,
++ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F,
++ 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F,
++ 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F,
++ 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F,
++ 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F,
++ 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F,
++ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F,
++ 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F,
++ 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F,
++ 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F,
++ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F,
++ 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F,
++ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F,
++ 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F,
++ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F,
++ 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F,
++ 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F,
++ 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F,
++ 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F,
++ 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F,
++ 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F,
++ 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F,
++ 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F,
++ 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F,
++ 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F,
++ 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
++ 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20,
++ 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
++ 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20,
++ 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
++ 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20,
++ 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
++ 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20,
++ 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
++ 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20,
++ 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
++ 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20,
++ 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
++ 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20,
++ 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
++ 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20,
++ 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
++ 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20,
++ 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
++ 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20,
++ 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20,
++ 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20,
++ 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20,
++ 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20,
++ 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20,
++ 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20,
++ 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20,
++ 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20,
++ 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20,
++ 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20,
++ 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20,
++ 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20,
++ 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
++ 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21,
++ 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
++ 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21,
++ 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
++ 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21,
++ 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
++ 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21,
++ 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
++ 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21,
++ 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
++ 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21,
++ 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
++ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21,
++ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
++ 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21,
++ 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
++ 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24,
++ 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24,
++ 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24,
++ 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C,
++ 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C,
++ 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C,
++ 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C,
++ 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C,
++ 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C,
++ 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C,
++ 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C,
++ 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C,
++ 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C,
++ 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C,
++ 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C,
++ 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C,
++ 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C,
++ 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C,
++ 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C,
++ 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C,
++ 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C,
++ 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C,
++ 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C,
++ 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C,
++ 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C,
++ 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C,
++ 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C,
++ 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C,
++ 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C,
++ 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10,
++ 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10,
++ 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10,
++ 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10,
++ 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10,
++ 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF,
++ 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF,
++ 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF,
++ 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF,
++ 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF,
++ 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF,
++ 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF,
++ 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF,
++ 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF,
++ 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF,
++ 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF,
++ 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF,
++ 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF,
++ 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF,
++ 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF,
++ 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF,
++ 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF,
++ 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF,
++ 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF,
++ 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF,
++ 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF,
++ 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF,
++ 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF,
++ 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF,
++ 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF
++};
+diff --git a/drivers/staging/exfat/exfat_version.h b/drivers/staging/exfat/exfat_version.h
+new file mode 100644
+index 00000000..ab6ce18b
+--- /dev/null
++++ b/drivers/staging/exfat/exfat_version.h
+@@ -0,0 +1 @@
++#define EXFAT_VERSION "1.2.4"
+--
+1.8.4.3.gca3854a
+