[U-Boot] [PATCH v2] FAT: Add FAT write feature

In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
The usage is similar with reading a file. First, fat_register_device function is called before file_fat_write function in order to set target partition. Then, file_fat_write function is invoked with desired file name, start ram address for writing data, and file size.
Signed-off-by: Donggeun Kim dg77.kim@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- Changes for v2: - update README file
README | 5 + fs/fat/Makefile | 1 + fs/fat/fat.c | 2 + fs/fat/fat_write.c | 1091 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/fat.h | 3 + 5 files changed, 1102 insertions(+), 0 deletions(-) create mode 100644 fs/fat/fat_write.c
diff --git a/README b/README index c6b179c..21baa6a 100644 --- a/README +++ b/README @@ -1144,6 +1144,11 @@ The following options need to be configured: to disable the command chpart. This is the default when you have not defined a custom partition
+- FAT(File Allocation Table) filesystem write function support: + CONFIG_FAT_WRITE + Support for saving memory data as a file + in FAT formatted partition + - Keyboard Support: CONFIG_ISA_KEYBOARD
diff --git a/fs/fat/Makefile b/fs/fat/Makefile index 93b6f07..9635d36 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -25,6 +25,7 @@ LIB = $(obj)libfat.o
AOBJS = COBJS-$(CONFIG_CMD_FAT) := fat.o +COBJS-$(CONFIG_FAT_WRITE):= fat_write.o
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_CMD_FAT) += file.o diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 756ac64..7cf173c 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -46,6 +46,7 @@ static void downcase (char *str) static block_dev_desc_t *cur_dev = NULL;
static unsigned long part_offset = 0; +static unsigned long part_size;
static int cur_part = 1;
@@ -99,6 +100,7 @@ int fat_register_device (block_dev_desc_t * dev_desc, int part_no) if (!get_partition_info(dev_desc, part_no, &info)) { part_offset = info.start; cur_part = part_no; + part_size = info.size; } else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { /* ok, we assume we are on a PBR only */ diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c new file mode 100644 index 0000000..e8e823b --- /dev/null +++ b/fs/fat/fat_write.c @@ -0,0 +1,1091 @@ +/* + * fat_write.c + * + * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <fat.h> +#include <asm/byteorder.h> +#include <part.h> +#include "fat.c" + +static void uppercase(char *str, int len) +{ + int i; + + for (i = 0; i < len; i++) { + TOUPPER(*str); + str++; + } +} + +static int total_sector; +static int disk_write(__u32 startblock, __u32 getsize, __u8 *bufptr) +{ + if (cur_dev == NULL) + return -1; + + if (startblock + getsize > total_sector) { + printf("error: overflow occurs\n"); + return -1; + } + + startblock += part_offset; + + if (cur_dev->block_read) { + return cur_dev->block_write(cur_dev->dev, startblock, getsize, + (unsigned long *) bufptr); + } + return -1; +} + +/* + * Set short name in directory entry + */ +static void set_name(dir_entry *dirent, const char *filename) +{ + char s_name[VFAT_MAXLEN_BYTES]; + char *period; + int period_location, len, i, ext_num; + + if (filename == NULL) + return; + + len = strlen(filename); + if (len == 0) + return; + + memcpy(s_name, filename, len); + uppercase(s_name, len); + + period = strchr(s_name, '.'); + if (period == NULL) { + period_location = len; + ext_num = 0; + } else { + period_location = period - s_name; + ext_num = len - period_location - 1; + } + + /* Pad spaces when the length of file name is shorter than eight */ + if (period_location < 8) { + memcpy(dirent->name, s_name, period_location); + for (i = period_location; i < 8; i++) + dirent->name[i] = ' '; + } else if (period_location == 8) { + memcpy(dirent->name, s_name, period_location); + } else { + memcpy(dirent->name, s_name, 6); + dirent->name[6] = '~'; + dirent->name[7] = '1'; + } + + if (ext_num < 3) { + memcpy(dirent->ext, s_name + period_location + 1, ext_num); + for (i = ext_num; i < 3; i++) + dirent->ext[i] = ' '; + } else + memcpy(dirent->ext, s_name + period_location + 1, 3); + + debug("name : %s\n", dirent->name); + debug("ext : %s\n", dirent->ext); +} + +/* + * Write fat buffer into block device + */ +static int flush_fat_buffer(fsdata *mydata) +{ + int getsize = FATBUFBLOCKS; + __u32 fatlength = mydata->fatlength; + __u8 *bufptr = mydata->fatbuf; + __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + /* Write FAT buf */ + if (disk_write(startblock, getsize, bufptr) < 0) { + debug("error: writing FAT blocks\n"); + return -1; + } + + return 0; +} + +/* + * Get the entry at index 'entry' in a FAT (12/16/32) table. + * On failure 0x00 is returned. + * When bufnum is changed, write back the previous fatbuf to the disk. + */ +static __u32 get_fatent_value(fsdata *mydata, __u32 entry) +{ + __u32 bufnum; + __u32 off16, offset; + __u32 ret = 0x00; + __u16 val1, val2; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + case 12: + bufnum = entry / FAT12BUFSIZE; + offset = entry - bufnum * FAT12BUFSIZE; + break; + + default: + /* Unsupported FAT size */ + return ret; + } + + debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", + mydata->fatsize, entry, entry, offset, offset); + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + if (getsize > fatlength) + getsize = fatlength; + + fatlength *= mydata->sect_size; /* We want it in bytes now */ + startblock += mydata->fat_sect; /* Offset from start of disk */ + + /* Write back the fatbuf to the disk */ + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return ret; + } + mydata->fatbufnum = bufnum; + } + + /* Get the actual entry from the table */ + switch (mydata->fatsize) { + case 32: + ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); + break; + case 16: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); + break; + case 12: + off16 = (offset * 3) / 4; + + switch (offset & 0x3) { + case 0: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); + ret &= 0xfff; + break; + case 1: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xf000; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x00ff; + ret = (val2 << 4) | (val1 >> 12); + break; + case 2: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xff00; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x000f; + ret = (val2 << 8) | (val1 >> 8); + break; + case 3: + ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + ret = (ret & 0xfff0) >> 4; + break; + default: + break; + } + break; + } + debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n", + mydata->fatsize, ret, entry, offset); + + return ret; +} + +#ifdef CONFIG_SUPPORT_VFAT +/* + * Set the file name information from 'name' into 'slotptr', + */ +static int str2slot(dir_slot *slotptr, const char *name, int *idx) +{ + int j, end_idx = 0; + + for (j = 0; j <= 8; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name0_4[j] = 0; + slotptr->name0_4[j + 1] = 0; + end_idx++; + goto name0_4; + } + slotptr->name0_4[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 10; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name5_10[j] = 0; + slotptr->name5_10[j + 1] = 0; + end_idx++; + goto name5_10; + } + slotptr->name5_10[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 2; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name11_12[j] = 0; + slotptr->name11_12[j + 1] = 0; + end_idx++; + goto name11_12; + } + slotptr->name11_12[j] = name[*idx]; + (*idx)++; + end_idx++; + } + + if (name[*idx] == 0x00) + return 1; + + return 0; +/* Not used characters are filled with 0xff 0xff */ +name0_4: + for (; end_idx < 5; end_idx++) { + slotptr->name0_4[end_idx * 2] = 0xff; + slotptr->name0_4[end_idx * 2 + 1] = 0xff; + } + end_idx = 5; +name5_10: + end_idx -= 5; + for (; end_idx < 6; end_idx++) { + slotptr->name5_10[end_idx * 2] = 0xff; + slotptr->name5_10[end_idx * 2 + 1] = 0xff; + } + end_idx = 11; +name11_12: + end_idx -= 11; + for (; end_idx < 2; end_idx++) { + slotptr->name11_12[end_idx * 2] = 0xff; + slotptr->name11_12[end_idx * 2 + 1] = 0xff; + } + + return 1; +} + +static int is_next_clust(fsdata *mydata, dir_entry *dentptr); +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); + +/* + * Fill dir_slot entries with appropriate name, id, and attr + * The real directory entry is returned by 'dentptr' + */ +static void +fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) +{ + dir_slot *slotptr = (dir_slot *)get_vfatname_block; + __u8 counter, checksum; + int idx = 0, ret; + char s_name[16]; + + /* Get short file name and checksum value */ + strncpy(s_name, (*dentptr)->name, 16); + checksum = mkcksum(s_name); + + do { + memset(slotptr, 0x00, sizeof(dir_slot)); + ret = str2slot(slotptr, l_name, &idx); + slotptr->id = ++counter; + slotptr->attr = ATTR_VFAT; + slotptr->alias_checksum = checksum; + slotptr++; + } while (ret == 0); + + slotptr--; + slotptr->id |= LAST_LONG_ENTRY_MASK; + + while (counter >= 1) { + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } + memcpy(*dentptr, slotptr, sizeof(dir_slot)); + (*dentptr)++; + slotptr--; + counter--; + } + + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } +} + +static __u32 dir_curclust; + +/* + * Extract the full long filename starting at 'retdent' (which is really + * a slot) into 'l_name'. If successful also copy the real directory entry + * into 'retdent' + * If additional adjacent cluster for directory entries is read into memory, + * then 'get_vfatname_block' is copied into 'get_dentfromdir_block' and + * the location of the real directory entry is returned by 'retdent' + * Return 0 on success, -1 otherwise. + */ +static int +get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, + dir_entry **retdent, char *l_name) +{ + dir_entry *realdent; + dir_slot *slotptr = (dir_slot *)(*retdent); + dir_slot *slotptr2 = NULL; + __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? + PREFETCH_BLOCKS : + mydata->clust_size); + __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; + int idx = 0, cur_position = 0; + + if (counter > VFAT_MAXSEQ) { + debug("Error: VFAT name is too long\n"); + return -1; + } + + while ((__u8 *)slotptr < buflimit) { + if (counter == 0) + break; + if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) + return -1; + slotptr++; + counter--; + } + + if ((__u8 *)slotptr >= buflimit) { + if (curclust == 0) + return -1; + curclust = get_fatent_value(mydata, dir_curclust); + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + printf("Invalid FAT entry\n"); + return -1; + } + + dir_curclust = curclust; + + if (get_cluster(mydata, curclust, get_vfatname_block, + mydata->clust_size * mydata->sect_size) != 0) { + debug("Error: reading directory block\n"); + return -1; + } + + slotptr2 = (dir_slot *)get_vfatname_block; + while (counter > 0) { + if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) + & 0xff) != counter) + return -1; + slotptr2++; + counter--; + } + + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr2; + while ((__u8 *)slotptr2 > get_vfatname_block) { + slotptr2--; + slot2str(slotptr2, l_name, &idx); + } + } else { + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr; + } + + do { + slotptr--; + if (slot2str(slotptr, l_name, &idx)) + break; + } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); + + l_name[idx] = '\0'; + if (*l_name == DELETED_FLAG) + *l_name = '\0'; + else if (*l_name == aRING) + *l_name = DELETED_FLAG; + downcase(l_name); + + /* Return the real directory entry */ + *retdent = realdent; + + if (slotptr2) { + memcpy(get_dentfromdir_block, get_vfatname_block, + mydata->clust_size * mydata->sect_size); + cur_position = (__u8 *)realdent - get_vfatname_block; + *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; + } + + return 0; +} + +#endif + +/* + * Set the entry at index 'entry' in a FAT (16/32) table. + */ +static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) +{ + __u32 bufnum, offset; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + default: + /* Unsupported FAT size */ + return -1; + } + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return -1; + } + mydata->fatbufnum = bufnum; + } + + /* Set the actual entry */ + switch (mydata->fatsize) { + case 32: + ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); + break; + case 16: + ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); + break; + default: + return -1; + } + + return 0; +} + +/* + * Determine the entry value at index 'entry' in a FAT (16/32) table + */ +static __u32 determine_fatent(fsdata *mydata, __u32 entry) +{ + __u32 next_fat, next_entry = entry + 1; + + while (1) { + next_fat = get_fatent_value(mydata, next_entry); + if (next_fat == 0) { + set_fatent_value(mydata, entry, next_entry); + break; + } + next_entry++; + } + debug("FAT%d: entry: %08x, entry_value: %04x\n", + mydata->fatsize, entry, next_entry); + + return next_entry; +} + +/* + * Write at most 'size' bytes from 'buffer' into the specified cluster. + * Return 0 on success, -1 otherwise. + */ +static int +set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, + unsigned long size) +{ + int idx = 0; + __u32 startsect; + + if (clustnum > 0) + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + else + startsect = mydata->rootdir_sect; + + debug("clustnum: %d, startsect: %d\n", clustnum, startsect); + + if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) { + debug("Error writing data\n"); + return -1; + } + + if (size % mydata->sect_size) { + __u8 tmpbuf[mydata->sect_size]; + + idx = size / mydata->sect_size; + buffer += idx * mydata->sect_size; + memcpy(tmpbuf, buffer, size % mydata->sect_size); + + if (disk_write(startsect + idx, 1, tmpbuf) < 0) { + debug("Error writing data\n"); + return -1; + } + + return 0; + } + + return 0; +} + +/* + * Find the first empty cluster + */ +static int find_empty_cluster(fsdata *mydata) +{ + __u32 fat_val, entry = 3; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val == 0) + break; + entry++; + } + + return entry; +} + +/* + * Write directory entries in 'get_dentfromdir_block' to block device + */ +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) +{ + int dir_newclust = 0; + + if (set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("error: wrinting directory entry\n"); + return; + } + dir_newclust = find_empty_cluster(mydata); + set_fatent_value(mydata, dir_curclust, dir_newclust); + if (mydata->fatsize == 32) + set_fatent_value(mydata, dir_newclust, 0xffffff8); + else if (mydata->fatsize == 16) + set_fatent_value(mydata, dir_newclust, 0xfff8); + + dir_curclust = dir_newclust; + + if (flush_fat_buffer(mydata) < 0) + return; + + memset(get_dentfromdir_block, 0x00, + mydata->clust_size * mydata->sect_size); + + *dentptr = (dir_entry *) get_dentfromdir_block; +} + +/* + * Set empty cluster from 'entry' to the end of a file + */ +static int clear_fatent(fsdata *mydata, __u32 entry) +{ + __u32 fat_val; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val != 0) + set_fatent_value(mydata, entry, 0); + else + break; + + if (fat_val == 0xfffffff || fat_val == 0xffff) + break; + + entry = fat_val; + } + + /* Flush fat buffer */ + if (flush_fat_buffer(mydata) < 0) + return -1; + + return 0; +} + +/* + * Write at most 'maxsize' bytes from 'buffer' into + * the file associated with 'dentptr' + * Return the number of bytes read or -1 on fatal errors. + */ +static int +set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, + unsigned long maxsize) +{ + unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; + __u32 curclust = START(dentptr); + __u32 endclust = 0, newclust = 0; + unsigned long actsize; + + debug("Filesize: %ld bytes\n", filesize); + + if (maxsize > 0 && filesize > maxsize) + filesize = maxsize; + + debug("%ld bytes\n", filesize); + + actsize = bytesperclust; + endclust = curclust; + do { + /* search for consecutive clusters */ + while (actsize < filesize) { + newclust = determine_fatent(mydata, endclust); + + if ((newclust - 1) != endclust) + goto getit; + + if (CHECK_CLUST(newclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", newclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + endclust = newclust; + actsize += bytesperclust; + } + /* actsize >= file size */ + actsize -= bytesperclust; + /* set remaining clusters */ + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + + /* set remaining bytes */ + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + actsize = filesize; + + if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += actsize; + + /* Mark end of file in FAT */ + if (mydata->fatsize == 16) + newclust = 0xffff; + else if (mydata->fatsize == 32) + newclust = 0xfffffff; + set_fatent_value(mydata, endclust, newclust); + + return gotsize; +getit: + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + actsize = bytesperclust; + curclust = endclust = newclust; + } while (1); +} + +/* + * Fill dir_entry + */ +static void fill_dentry(fsdata *mydata, dir_entry *dentptr, + const char *filename, __u32 start_cluster, __u32 size, __u8 attr) +{ + if (mydata->fatsize == 32) + dentptr->starthi = + cpu_to_le16((start_cluster & 0xffff0000) >> 16); + dentptr->start = cpu_to_le16(start_cluster & 0xffff); + dentptr->size = cpu_to_le32(size); + + dentptr->attr = attr; + + set_name(dentptr, filename); +} + +/* + * Check whether adding a file makes the file system to + * exceed the size of the block device + * Return -1 when overflow occurs, otherwise return 0 + */ +static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size) +{ + __u32 startsect, sect_num; + + if (clustnum > 0) { + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + } else { + startsect = mydata->rootdir_sect; + } + + sect_num = size / mydata->sect_size; + if (size % mydata->sect_size) + sect_num++; + + if (startsect + sect_num > total_sector) + return -1; + + return 0; +} + +/* + * Check if adding several entries exceed one cluster boundary + */ +static int is_next_clust(fsdata *mydata, dir_entry *dentptr) +{ + int cur_position; + + cur_position = (__u8 *)dentptr - get_dentfromdir_block; + + if (cur_position >= mydata->clust_size * mydata->sect_size) + return 1; + else + return 0; +} + +static dir_entry *empty_dentptr; +/* + * Find a directory entry based on filename or start cluster number + * If the directory entry is not found, + * the new position for writing a directory entry will be returned + */ +static dir_entry *find_directory_entry(fsdata *mydata, int startsect, + char *filename, dir_entry *retdent, __u32 start) +{ + __u16 prevcksum = 0xffff; + __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; + + debug("get_dentfromdir: %s\n", filename); + + while (1) { + dir_entry *dentptr; + + int i; + + if (get_cluster(mydata, curclust, get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("Error: reading directory block\n"); + return NULL; + } + + dentptr = (dir_entry *)get_dentfromdir_block; + + dir_curclust = curclust; + + for (i = 0; i < DIRENTSPERCLUST; i++) { + char s_name[14], l_name[VFAT_MAXLEN_BYTES]; + + l_name[0] = '\0'; + if (dentptr->name[0] == DELETED_FLAG) { + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + if ((dentptr->attr & ATTR_VOLUME)) { +#ifdef CONFIG_SUPPORT_VFAT + if ((dentptr->attr & ATTR_VFAT) && + (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { + prevcksum = + ((dir_slot *)dentptr)->alias_checksum; + get_long_file_name(mydata, curclust, + get_dentfromdir_block, + &dentptr, l_name); + debug("vfatname: |%s|\n", l_name); + } else +#endif + { + /* Volume label or VFAT entry */ + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + } + if (dentptr->name[0] == 0) { + debug("Dentname == NULL - %d\n", i); + empty_dentptr = dentptr; + return NULL; + } + + get_name(dentptr, s_name); + + if (strcmp(filename, s_name) + && strcmp(filename, l_name)) { + debug("Mismatch: |%s|%s|\n", + s_name, l_name); + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + + memcpy(retdent, dentptr, sizeof(dir_entry)); + + debug("DentName: %s", s_name); + debug(", start: 0x%x", START(dentptr)); + debug(", size: 0x%x %s\n", + FAT2CPU32(dentptr->size), + (dentptr->attr & ATTR_DIR) ? + "(DIR)" : ""); + + return dentptr; + } + + curclust = get_fatent_value(mydata, dir_curclust); + if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) { + empty_dentptr = dentptr; + return NULL; + } + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return NULL; + } + } + + return NULL; +} + +static int do_fat_write(const char *filename, void *buffer, + unsigned long size) +{ + dir_entry *dentptr, *retdent; + dir_slot *slotptr; + __u32 startsect; + __u32 start_cluster; + boot_sector bs; + volume_info volinfo; + fsdata datablock; + fsdata *mydata = &datablock; + int cursect; + int root_cluster, ret = -1, name_len; + char l_filename[VFAT_MAXLEN_BYTES]; + + dir_curclust = 0; + + if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { + debug("error: reading boot sector\n"); + return -1; + } + + total_sector = bs.total_sect; + if (total_sector == 0) + total_sector = part_size; + + root_cluster = bs.root_cluster; + + if (mydata->fatsize == 32) + mydata->fatlength = bs.fat32_length; + else + mydata->fatlength = bs.fat_length; + + mydata->fat_sect = bs.reserved; + + cursect = mydata->rootdir_sect + = mydata->fat_sect + mydata->fatlength * bs.fats; + + mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; + mydata->clust_size = bs.cluster_size; + + if (mydata->fatsize == 32) { + mydata->data_begin = mydata->rootdir_sect - + (mydata->clust_size * 2); + } else { + int rootdir_size; + + rootdir_size = ((bs.dir_entries[1] * (int)256 + + bs.dir_entries[0]) * + sizeof(dir_entry)) / + mydata->sect_size; + mydata->data_begin = mydata->rootdir_sect + + rootdir_size - + (mydata->clust_size * 2); + } + + mydata->fatbufnum = -1; + mydata->fatbuf = malloc(FATBUFSIZE); + if (mydata->fatbuf == NULL) { + debug("Error: allocating memory\n"); + return -1; + } + + if (disk_read(cursect, + (mydata->fatsize == 32) ? + (mydata->clust_size) : + PREFETCH_BLOCKS, do_fat_read_block) < 0) { + debug("Error: reading rootdir block\n"); + goto exit; + } + dentptr = (dir_entry *) do_fat_read_block; + + name_len = strlen(filename); + memcpy(l_filename, filename, name_len); + downcase(l_filename); + + startsect = mydata->rootdir_sect; + retdent = find_directory_entry(mydata, startsect, + l_filename, dentptr, 0); + if (retdent) { + /* Update file size and start_cluster in a directory entry */ + retdent->size = cpu_to_le32(size); + start_cluster = FAT2CPU16(retdent->start); + if (mydata->fatsize == 32) + start_cluster |= + (FAT2CPU16(retdent->starthi) << 16); + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + ret = clear_fatent(mydata, start_cluster); + if (ret) { + printf("Error: clearing FAT entries\n"); + goto exit; + } + + ret = set_contents(mydata, retdent, buffer, size); + if (ret) { + printf("Error: writing contents\n"); + goto exit; + } + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: wrinting directory entry\n"); + goto exit; + } + } else { + slotptr = (dir_slot *)empty_dentptr; + + /* Set short name to set alias checksum field in dir_slot */ + set_name(empty_dentptr, filename); + fill_dir_slot(mydata, &empty_dentptr, filename); + + ret = start_cluster = find_empty_cluster(mydata); + if (ret < 0) { + printf("Error: finding empty cluster\n"); + goto exit; + } + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + /* Set attribute as archieve for regular file */ + fill_dentry(mydata, empty_dentptr, filename, + start_cluster, size, 0x20); + + ret = set_contents(mydata, empty_dentptr, buffer, size); + if (ret) { + printf("Error: writing contents\n"); + goto exit; + } + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: writing directory entry\n"); + goto exit; + } + } + +exit: + free(mydata->fatbuf); + return ret; +} + +int file_fat_write(const char *filename, void *buffer, unsigned long maxsize) +{ + printf("writing %s\n", filename); + return do_fat_write(filename, buffer, maxsize); +} + diff --git a/include/fat.h b/include/fat.h index 15dbf10..4c92442 100644 --- a/include/fat.h +++ b/include/fat.h @@ -99,6 +99,8 @@ #endif
#define TOLOWER(c) if((c) >= 'A' && (c) <= 'Z'){(c)+=('a' - 'A');} +#define TOUPPER(c) if ((c) >= 'a' && (c) <= 'z') \ + (c) -= ('a' - 'A'); #define START(dent) (FAT2CPU16((dent)->start) \ + (mydata->fatsize != 32 ? 0 : \ (FAT2CPU16((dent)->starthi) << 16))) @@ -210,4 +212,5 @@ long file_fat_read(const char *filename, void *buffer, unsigned long maxsize); const char *file_getfsname(int idx); int fat_register_device(block_dev_desc_t *dev_desc, int part_no);
+int file_fat_write(const char *filename, void *buffer, unsigned long maxsize); #endif /* _FAT_H_ */

Dear Donggeun Kim,
In message 1319526928-7927-1-git-send-email-dg77.kim@samsung.com you wrote:
In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
The usage is similar with reading a file. First, fat_register_device function is called before file_fat_write function in order to set target partition. Then, file_fat_write function is invoked with desired file name, start ram address for writing data, and file size.
Signed-off-by: Donggeun Kim dg77.kim@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
Changes for v2:
- update README file
README | 5 + fs/fat/Makefile | 1 + fs/fat/fat.c | 2 + fs/fat/fat_write.c | 1091 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/fat.h | 3 + 5 files changed, 1102 insertions(+), 0 deletions(-) create mode 100644 fs/fat/fat_write.c
Applied, thanks.
Best regards,
Wolfgang Denk

Dear Donggeun Kim,
In message 1319526928-7927-1-git-send-email-dg77.kim@samsung.com you wrote:
In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
The usage is similar with reading a file. First, fat_register_device function is called before file_fat_write function in order to set target partition. Then, file_fat_write function is invoked with desired file name, start ram address for writing data, and file size.
Signed-off-by: Donggeun Kim dg77.kim@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
Changes for v2:
- update README file
README | 5 + fs/fat/Makefile | 1 + fs/fat/fat.c | 2 + fs/fat/fat_write.c | 1091 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/fat.h | 3 + 5 files changed, 1102 insertions(+), 0 deletions(-) create mode 100644 fs/fat/fat_write.c
Applied, thanks.
DAMN :-(
Dongguen,
I now get this warning on many boards: fat.c:49:22: warning: 'part_size' defined but not used
please FIX !
Also, can you avoid using global variables, it's stupid?
Cheers

On Tue, Oct 25, 2011 at 09:15, Donggeun Kim wrote:
In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
i thought Wolfgang NAK-ed FS write patches in the past ...
--- a/fs/fat/Makefile +++ b/fs/fat/Makefile
COBJS-$(CONFIG_CMD_FAT) := fat.o +COBJS-$(CONFIG_FAT_WRITE):= fat_write.o
missing whitespace before that ":="
--- /dev/null +++ b/fs/fat/fat_write.c
+/*
- fat_write.c
- R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
- See file CREDITS for list of people who contributed to this
- project.
- 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., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
there's no actual copyright here. you wrote this all from scratch ?
+static void set_name(dir_entry *dirent, const char *filename) +{
- int period_location, len, i, ext_num;
- len = strlen(filename);
i/period_location/len should be size_t, not int
- memcpy(s_name, filename, len);
- uppercase(s_name, len);
you use this uppercase() func exactly once, and do a memcpy right before. drop the memcpy, and inline the uppercase func and use the proper toupper helper from linux/ctype.h
- /* Pad spaces when the length of file name is shorter than eight */
- if (period_location < 8) {
- memcpy(dirent->name, s_name, period_location);
- for (i = period_location; i < 8; i++)
- dirent->name[i] = ' ';
- } else if (period_location == 8) {
- memcpy(dirent->name, s_name, period_location);
- } else {
- memcpy(dirent->name, s_name, 6);
- dirent->name[6] = '~';
- dirent->name[7] = '1';
- }
err, doesn't this "~1" hardcode assume that there is no "~1" file already existing ? and if there happens to be, you blow it away/corrupt the fs ?
+static __u32 get_fatent_value(fsdata *mydata, __u32 entry) +{
- /* Get the actual entry from the table */
- switch (mydata->fatsize) {
- case 32:
- ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
- break;
- case 16:
- ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
ugh, whoever dreamt up these FAT2CPU* macros should get smacked. these need to get scrubbed from fat.h and all code in u-boot and replaced with proper get/put unaligned macros from include/linux/.
+static int is_next_clust(fsdata *mydata, dir_entry *dentptr); +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
re-order these tiny funcs in the file to avoid having to use prototypes
+/*
- Fill dir_slot entries with appropriate name, id, and attr
- The real directory entry is returned by 'dentptr'
- */
+static void +fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) +{
- dir_slot *slotptr = (dir_slot *)get_vfatname_block;
- __u8 counter, checksum;
- int idx = 0, ret;
- char s_name[16];
- /* Get short file name and checksum value */
- strncpy(s_name, (*dentptr)->name, 16);
- checksum = mkcksum(s_name);
- do {
- memset(slotptr, 0x00, sizeof(dir_slot));
- ret = str2slot(slotptr, l_name, &idx);
- slotptr->id = ++counter;
- slotptr->attr = ATTR_VFAT;
- slotptr->alias_checksum = checksum;
- slotptr++;
- } while (ret == 0);
- slotptr--;
- slotptr->id |= LAST_LONG_ENTRY_MASK;
- while (counter >= 1) {
- if (is_next_clust(mydata, *dentptr)) {
- /* A new cluster is allocated for directory table */
- flush_dir_table(mydata, dentptr);
- }
- memcpy(*dentptr, slotptr, sizeof(dir_slot));
- (*dentptr)++;
- slotptr--;
- counter--;
- }
- if (is_next_clust(mydata, *dentptr)) {
- /* A new cluster is allocated for directory table */
- flush_dir_table(mydata, dentptr);
- }
+}
+static __u32 dir_curclust;
all these global variables make me cry. i guess it's a good thing u-boot is single threaded.
+static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) +{ ...
- /* Set the actual entry */
- switch (mydata->fatsize) {
- case 32:
- ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
- break;
- case 16:
- ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
use proper put_unaligned helpers from include/linux/
+static int is_next_clust(fsdata *mydata, dir_entry *dentptr) +{ ...
- cur_position = (__u8 *)dentptr - get_dentfromdir_block;
you've got a lot of random casts throughout this file. makes me really wonder as to its sanity.
- printf("Error: flush fat buffer\n");
all of these printf() calls without any format args should be puts() instead
--- a/include/fat.h +++ b/include/fat.h
#define TOLOWER(c) if((c) >= 'A' && (c) <= 'Z'){(c)+=('a' - 'A');} +#define TOUPPER(c) if ((c) >= 'a' && (c) <= 'z') \
(c) -= ('a' - 'A');
why do these exist ? we already have tolower/toupper in linux/ctype.h, and macros shouldn't be embedding ugly if/assignments. -mike

Dear Mike Frysinger,
In message CAJaTeToXsLLiqKb1NZpi457OW39admnPtWSrRZVkpRPNXJ6wiA@mail.gmail.com you wrote:
On Tue, Oct 25, 2011 at 09:15, Donggeun Kim wrote:
In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
i thought Wolfgang NAK-ed FS write patches in the past ...
If I did, then for technical issues that needed to be cleaned up. I think write support is a good thing to have.
COBJS-$(CONFIG_CMD_FAT) := fat.o +COBJS-$(CONFIG_FAT_WRITE):= fat_write.o
missing whitespace before that ":="
Argh.. THe patch has already been applied, so this needs to be cleaned up in a separate patch now.
Best regards,
Wolfgang Denk

On Thu, Oct 27, 2011 at 22:55, Wolfgang Denk wrote:
Mike Frysinger wrote:
On Tue, Oct 25, 2011 at 09:15, Donggeun Kim wrote:
In some cases, saving data in RAM as a file with FAT format is required. This patch allows the file to be written in FAT formatted partition.
i thought Wolfgang NAK-ed FS write patches in the past ...
If I did, then for technical issues that needed to be cleaned up. I think write support is a good thing to have.
np. i thought i recalled old patches/proposals like being able to `mv` on a jffs2 being NAK-ed on the grounds that if you want to modify a FS, you should boot into a real OS. maybe i'm just making this all up in my head though. -mike

Commit c30a15e "FAT: Add FAT write feature" introduced a compiler warning. Fix this.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Donggeun Kim dg77.kim@samsung.com Cc: Kyungmin Park kyungmin.park@samsung.com ---
fs/fat/fat.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 7cf173c..756ac64 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -46,7 +46,6 @@ static void downcase (char *str) static block_dev_desc_t *cur_dev = NULL;
static unsigned long part_offset = 0; -static unsigned long part_size;
static int cur_part = 1;
@@ -100,7 +99,6 @@ int fat_register_device (block_dev_desc_t * dev_desc, int part_no) if (!get_partition_info(dev_desc, part_no, &info)) { part_offset = info.start; cur_part = part_no; - part_size = info.size; } else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { /* ok, we assume we are on a PBR only */

On Thursday, October 27, 2011 02:10:58 PM Wolfgang Denk wrote:
Commit c30a15e "FAT: Add FAT write feature" introduced a compiler warning. Fix this.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Donggeun Kim dg77.kim@samsung.com Cc: Kyungmin Park kyungmin.park@samsung.com
fs/fat/fat.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 7cf173c..756ac64 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -46,7 +46,6 @@ static void downcase (char *str) static block_dev_desc_t *cur_dev = NULL;
static unsigned long part_offset = 0; -static unsigned long part_size;
static int cur_part = 1;
@@ -100,7 +99,6 @@ int fat_register_device (block_dev_desc_t * dev_desc, int part_no) if (!get_partition_info(dev_desc, part_no, &info)) { part_offset = info.start; cur_part = part_no;
} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0)part_size = info.size;
|| (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { /* ok, we assume we are on a PBR only */
Hi Wolfgang,
I know it's rather late to comment on this, but this patch breaks FAT write support.
-Aaron

Dear Aaron Williams,
In message 201112122215.11610.Aaron.Williams@cavium.com you wrote:
Commit c30a15e "FAT: Add FAT write feature" introduced a compiler warning. Fix this.
...
I know it's rather late to comment on this, but this patch breaks FAT write support.
How can it break something we don't have?
Currently there is no write support for (V)FAT file systems in mainline.
The commit removes a static variable of file scope, that was used in a single place, where a value was assigned to it. There was no place anywhere in the code twhere else this variable was referenced. So how can this break anything?
Please elucidate.
Best regards,
Wolfgang Denk

Hello all,
On Tue, 13 Dec 2011 09:18:15 +0100 Wolfgang Denk wd@denx.de wrote:
Dear Aaron Williams,
In message 201112122215.11610.Aaron.Williams@cavium.com you wrote:
Commit c30a15e "FAT: Add FAT write feature" introduced a compiler warning. Fix this.
...
I know it's rather late to comment on this, but this patch breaks FAT write support.
How can it break something we don't have?
Currently there is no write support for (V)FAT file systems in mainline.
The commit removes a static variable of file scope, that was used in a single place, where a value was assigned to it. There was no place anywhere in the code twhere else this variable was referenced. So how can this break anything?
Please elucidate.
The FAT write support as submitted and included seems to be broken. The removed variable is referenced in fs/fat/fat_write.c, but in fs/fat/fat.c it is declared as static. This issue didn't show up because no board config file in mainline defines CONFIG_FAT_WRITE.
@ Donggeun Kim Could you please submit a patch fixing this?
Thanks, Anatolij

Dear Anatolij Gustschin,
In message 20111213095358.5601b9ce@wker you wrote:
The FAT write support as submitted and included seems to be broken. The removed variable is referenced in fs/fat/fat_write.c, but in fs/fat/fat.c it is declared as static. This issue didn't show up because no board config file in mainline defines CONFIG_FAT_WRITE.
It's not only that. It appears that this code is also not hooked upo to any command a user could call. At least, I cannot see anything like that in common/cmd_fat.c or so...
@ Donggeun Kim Could you please submit a patch fixing this?
I guess this needs more fixing...
Best regards,
Wolfgang Denk

On 2011년 12월 13일 15:15, Aaron Williams wrote:
On Thursday, October 27, 2011 02:10:58 PM Wolfgang Denk wrote:
Commit c30a15e "FAT: Add FAT write feature" introduced a compiler warning. Fix this.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Donggeun Kim dg77.kim@samsung.com Cc: Kyungmin Park kyungmin.park@samsung.com
fs/fat/fat.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 7cf173c..756ac64 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -46,7 +46,6 @@ static void downcase (char *str) static block_dev_desc_t *cur_dev = NULL;
static unsigned long part_offset = 0; -static unsigned long part_size;
static int cur_part = 1;
@@ -100,7 +99,6 @@ int fat_register_device (block_dev_desc_t * dev_desc, int part_no) if (!get_partition_info(dev_desc, part_no, &info)) { part_offset = info.start; cur_part = part_no;
} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0)part_size = info.size;
|| (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { /* ok, we assume we are on a PBR only */
Hi Wolfgang,
I know it's rather late to comment on this, but this patch breaks FAT write support.
-Aaron
Hi, Wolfgang,
I've overlooked the patch before. If 'part_size' variable is not set in fat_register_device function, FAT write feature would not work properly. So, I will send a patch to define the variable only when FAT_WRITE is enabled. It can avoid compile warning.
Thanks. -Donggeun
participants (6)
-
Aaron Williams
-
Anatolij Gustschin
-
Donggeun Kim
-
Marek Vasut
-
Mike Frysinger
-
Wolfgang Denk