mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/enable-datetime-modification-fatfs' into 'master'
fatfsgen.py: enabled date and time for fatfs Closes IDF-4092 See merge request espressif/esp-idf!17519
This commit is contained in:
commit
d612c71be5
@ -3,13 +3,14 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from fatfsgen_utils.fat import FAT
|
from fatfsgen_utils.fat import FAT
|
||||||
from fatfsgen_utils.fatfs_parser import FATFSParser
|
from fatfsgen_utils.fatfs_parser import FATFSParser
|
||||||
from fatfsgen_utils.fatfs_state import FATFSState
|
from fatfsgen_utils.fatfs_state import FATFSState
|
||||||
from fatfsgen_utils.fs_object import Directory
|
from fatfsgen_utils.fs_object import Directory
|
||||||
from fatfsgen_utils.utils import (BYTES_PER_DIRECTORY_ENTRY, FAT32, generate_4bytes_random,
|
from fatfsgen_utils.utils import (BYTES_PER_DIRECTORY_ENTRY, FAT32, FATFS_INCEPTION, generate_4bytes_random,
|
||||||
get_args_for_partition_generator, pad_string, read_filesystem)
|
get_args_for_partition_generator, pad_string, read_filesystem)
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ class FATFS:
|
|||||||
sectors_per_fat: int = 1,
|
sectors_per_fat: int = 1,
|
||||||
hidden_sectors: int = 0,
|
hidden_sectors: int = 0,
|
||||||
long_names_enabled: bool = False,
|
long_names_enabled: bool = False,
|
||||||
|
use_default_datetime: bool = True,
|
||||||
entry_size: int = 32,
|
entry_size: int = 32,
|
||||||
num_heads: int = 0xff,
|
num_heads: int = 0xff,
|
||||||
oem_name: str = 'MSDOS5.0',
|
oem_name: str = 'MSDOS5.0',
|
||||||
@ -60,7 +62,8 @@ class FATFS:
|
|||||||
sec_per_track=sec_per_track,
|
sec_per_track=sec_per_track,
|
||||||
long_names_enabled=long_names_enabled,
|
long_names_enabled=long_names_enabled,
|
||||||
volume_label=volume_label,
|
volume_label=volume_label,
|
||||||
oem_name=oem_name)
|
oem_name=oem_name,
|
||||||
|
use_default_datetime=use_default_datetime)
|
||||||
binary_image: bytes = bytearray(
|
binary_image: bytes = bytearray(
|
||||||
read_filesystem(binary_image_path) if binary_image_path else self.create_empty_fatfs())
|
read_filesystem(binary_image_path) if binary_image_path else self.create_empty_fatfs())
|
||||||
self.state.binary_image = binary_image
|
self.state.binary_image = binary_image
|
||||||
@ -74,17 +77,28 @@ class FATFS:
|
|||||||
fatfs_state=self.state)
|
fatfs_state=self.state)
|
||||||
self.root_directory.init_directory()
|
self.root_directory.init_directory()
|
||||||
|
|
||||||
def create_file(self, name: str, extension: str = '', path_from_root: Optional[List[str]] = None) -> None:
|
def create_file(self, name: str,
|
||||||
|
extension: str = '',
|
||||||
|
path_from_root: Optional[List[str]] = None,
|
||||||
|
object_timestamp_: datetime = FATFS_INCEPTION) -> None:
|
||||||
# when path_from_root is None the dir is root
|
# when path_from_root is None the dir is root
|
||||||
self.root_directory.new_file(name=name, extension=extension, path_from_root=path_from_root)
|
self.root_directory.new_file(name=name,
|
||||||
|
extension=extension,
|
||||||
|
path_from_root=path_from_root,
|
||||||
|
object_timestamp_=object_timestamp_)
|
||||||
|
|
||||||
def create_directory(self, name: str, path_from_root: Optional[List[str]] = None) -> None:
|
def create_directory(self, name: str,
|
||||||
|
path_from_root: Optional[List[str]] = None,
|
||||||
|
object_timestamp_: datetime = FATFS_INCEPTION) -> None:
|
||||||
# when path_from_root is None the dir is root
|
# when path_from_root is None the dir is root
|
||||||
parent_dir = self.root_directory
|
parent_dir = self.root_directory
|
||||||
if path_from_root:
|
if path_from_root:
|
||||||
parent_dir = self.root_directory.recursive_search(path_from_root, self.root_directory)
|
parent_dir = self.root_directory.recursive_search(path_from_root, self.root_directory)
|
||||||
|
|
||||||
self.root_directory.new_directory(name=name, parent=parent_dir, path_from_root=path_from_root)
|
self.root_directory.new_directory(name=name,
|
||||||
|
parent=parent_dir,
|
||||||
|
path_from_root=path_from_root,
|
||||||
|
object_timestamp_=object_timestamp_)
|
||||||
|
|
||||||
def write_content(self, path_from_root: List[str], content: bytes) -> None:
|
def write_content(self, path_from_root: List[str], content: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
@ -140,16 +154,23 @@ class FATFS:
|
|||||||
|
|
||||||
normal_path = os.path.normpath(folder_relative_path)
|
normal_path = os.path.normpath(folder_relative_path)
|
||||||
split_path = normal_path.split(os.sep)
|
split_path = normal_path.split(os.sep)
|
||||||
|
object_timestamp = datetime.fromtimestamp(os.path.getctime(real_path))
|
||||||
|
|
||||||
if os.path.isfile(real_path):
|
if os.path.isfile(real_path):
|
||||||
with open(real_path, 'rb') as file:
|
with open(real_path, 'rb') as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
file_name, extension = os.path.splitext(split_path[-1])
|
file_name, extension = os.path.splitext(split_path[-1])
|
||||||
extension = extension[1:] # remove the dot from the extension
|
extension = extension[1:] # remove the dot from the extension
|
||||||
self.create_file(name=file_name, extension=extension, path_from_root=split_path[1:-1] or None)
|
self.create_file(name=file_name,
|
||||||
|
extension=extension,
|
||||||
|
path_from_root=split_path[1:-1] or None,
|
||||||
|
object_timestamp_=object_timestamp)
|
||||||
self.write_content(split_path[1:], content)
|
self.write_content(split_path[1:], content)
|
||||||
elif os.path.isdir(real_path):
|
elif os.path.isdir(real_path):
|
||||||
if not is_dir:
|
if not is_dir:
|
||||||
self.create_directory(split_path[-1], split_path[1:-1])
|
self.create_directory(name=split_path[-1],
|
||||||
|
path_from_root=split_path[1:-1],
|
||||||
|
object_timestamp_=object_timestamp)
|
||||||
|
|
||||||
# sorting files for better testability
|
# sorting files for better testability
|
||||||
dir_content = list(sorted(os.listdir(real_path)))
|
dir_content = list(sorted(os.listdir(real_path)))
|
||||||
@ -171,7 +192,8 @@ def main() -> None:
|
|||||||
size=args.partition_size,
|
size=args.partition_size,
|
||||||
root_entry_count=args.root_entry_count,
|
root_entry_count=args.root_entry_count,
|
||||||
explicit_fat_type=args.fat_type,
|
explicit_fat_type=args.fat_type,
|
||||||
long_names_enabled=args.long_name_support)
|
long_names_enabled=args.long_name_support,
|
||||||
|
use_default_datetime=args.use_default_datetime)
|
||||||
|
|
||||||
fatfs.generate(args.input_directory)
|
fatfs.generate(args.input_directory)
|
||||||
fatfs.write_filesystem(args.output_file)
|
fatfs.write_filesystem(args.output_file)
|
||||||
|
@ -7,7 +7,8 @@ from construct import Const, Int8ul, Int16ul, Int32ul, PaddedString, Struct
|
|||||||
|
|
||||||
from .exceptions import LowerCaseException, TooLongNameException
|
from .exceptions import LowerCaseException, TooLongNameException
|
||||||
from .fatfs_state import FATFSState
|
from .fatfs_state import FATFSState
|
||||||
from .utils import MAX_EXT_SIZE, MAX_NAME_SIZE, is_valid_fatfs_name, pad_string
|
from .utils import (DATETIME, FATFS_INCEPTION, MAX_EXT_SIZE, MAX_NAME_SIZE, SHORT_NAMES_ENCODING, build_date_entry,
|
||||||
|
build_time_entry, is_valid_fatfs_name, pad_string)
|
||||||
|
|
||||||
|
|
||||||
class Entry:
|
class Entry:
|
||||||
@ -36,18 +37,22 @@ class Entry:
|
|||||||
SHORT_ENTRY: int = -1
|
SHORT_ENTRY: int = -1
|
||||||
SHORT_ENTRY_LN: int = 0
|
SHORT_ENTRY_LN: int = 0
|
||||||
|
|
||||||
|
# The 1st January 1980 00:00:00
|
||||||
|
DEFAULT_DATE: DATETIME = (FATFS_INCEPTION.year, FATFS_INCEPTION.month, FATFS_INCEPTION.day)
|
||||||
|
DEFAULT_TIME: DATETIME = (FATFS_INCEPTION.hour, FATFS_INCEPTION.minute, FATFS_INCEPTION.second)
|
||||||
|
|
||||||
ENTRY_FORMAT_SHORT_NAME = Struct(
|
ENTRY_FORMAT_SHORT_NAME = Struct(
|
||||||
'DIR_Name' / PaddedString(MAX_NAME_SIZE, 'utf-8'),
|
'DIR_Name' / PaddedString(MAX_NAME_SIZE, SHORT_NAMES_ENCODING),
|
||||||
'DIR_Name_ext' / PaddedString(MAX_EXT_SIZE, 'utf-8'),
|
'DIR_Name_ext' / PaddedString(MAX_EXT_SIZE, SHORT_NAMES_ENCODING),
|
||||||
'DIR_Attr' / Int8ul,
|
'DIR_Attr' / Int8ul,
|
||||||
'DIR_NTRes' / Const(b'\x00'),
|
'DIR_NTRes' / Const(b'\x00'),
|
||||||
'DIR_CrtTimeTenth' / Const(b'\x00'),
|
'DIR_CrtTimeTenth' / Const(b'\x00'), # ignored by esp-idf fatfs library
|
||||||
'DIR_CrtTime' / Const(b'\x00\x00'),
|
'DIR_CrtTime' / Int16ul, # ignored by esp-idf fatfs library
|
||||||
'DIR_CrtDate' / Const(b'\x21\x00'),
|
'DIR_CrtDate' / Int16ul, # ignored by esp-idf fatfs library
|
||||||
'DIR_LstAccDate' / Const(b'\x00\x00'),
|
'DIR_LstAccDate' / Int16ul, # must be same as DIR_WrtDate
|
||||||
'DIR_FstClusHI' / Const(b'\x00\x00'),
|
'DIR_FstClusHI' / Const(b'\x00\x00'),
|
||||||
'DIR_WrtTime' / Const(b'\x00\x00'),
|
'DIR_WrtTime' / Int16ul,
|
||||||
'DIR_WrtDate' / Const(b'\x21\x00'),
|
'DIR_WrtDate' / Int16ul,
|
||||||
'DIR_FstClusLO' / Int16ul,
|
'DIR_FstClusLO' / Int16ul,
|
||||||
'DIR_FileSize' / Int32ul,
|
'DIR_FileSize' / Int32ul,
|
||||||
)
|
)
|
||||||
@ -117,6 +122,8 @@ class Entry:
|
|||||||
entity_type: int,
|
entity_type: int,
|
||||||
entity_extension: str = '',
|
entity_extension: str = '',
|
||||||
size: int = 0,
|
size: int = 0,
|
||||||
|
date: DATETIME = DEFAULT_DATE,
|
||||||
|
time: DATETIME = DEFAULT_TIME,
|
||||||
lfn_order: int = SHORT_ENTRY,
|
lfn_order: int = SHORT_ENTRY,
|
||||||
lfn_names: Optional[List[bytes]] = None,
|
lfn_names: Optional[List[bytes]] = None,
|
||||||
lfn_checksum_: int = 0,
|
lfn_checksum_: int = 0,
|
||||||
@ -126,6 +133,8 @@ class Entry:
|
|||||||
:param entity_name: name recorded in the entry
|
:param entity_name: name recorded in the entry
|
||||||
:param entity_extension: extension recorded in the entry
|
:param entity_extension: extension recorded in the entry
|
||||||
:param size: size of the content of the file
|
:param size: size of the content of the file
|
||||||
|
:param date: denotes year (actual year minus 1980), month number day of the month (minimal valid is (0, 1, 1))
|
||||||
|
:param time: denotes hour, minute and second with granularity 2 seconds (sec // 2)
|
||||||
:param entity_type: type of the entity (file [0x20] or directory [0x10])
|
:param entity_type: type of the entity (file [0x20] or directory [0x10])
|
||||||
:param lfn_order: if long names support is enabled, defines order in long names entries sequence (-1 for short)
|
:param lfn_order: if long names support is enabled, defines order in long names entries sequence (-1 for short)
|
||||||
:param lfn_names: if the entry is dedicated for long names the lfn_names contains
|
:param lfn_names: if the entry is dedicated for long names the lfn_names contains
|
||||||
@ -137,11 +146,17 @@ class Entry:
|
|||||||
|
|
||||||
:raises LowerCaseException: In case when long_names_enabled is set to False and filename exceeds 8 chars
|
:raises LowerCaseException: In case when long_names_enabled is set to False and filename exceeds 8 chars
|
||||||
for name or 3 chars for extension the exception is raised
|
for name or 3 chars for extension the exception is raised
|
||||||
|
:raises TooLongNameException: When long_names_enabled is set to False and name doesn't fit to 8.3 filename
|
||||||
|
an exception is raised
|
||||||
"""
|
"""
|
||||||
valid_full_name: bool = is_valid_fatfs_name(entity_name) and is_valid_fatfs_name(entity_extension)
|
valid_full_name: bool = is_valid_fatfs_name(entity_name) and is_valid_fatfs_name(entity_extension)
|
||||||
if not (valid_full_name or lfn_order >= 0):
|
if not (valid_full_name or lfn_order >= 0):
|
||||||
raise LowerCaseException('Lower case is not supported in short name entry, use upper case.')
|
raise LowerCaseException('Lower case is not supported in short name entry, use upper case.')
|
||||||
|
|
||||||
|
if self.fatfs_state.use_default_datetime:
|
||||||
|
date = self.DEFAULT_DATE
|
||||||
|
time = self.DEFAULT_TIME
|
||||||
|
|
||||||
# clean entry before allocation
|
# clean entry before allocation
|
||||||
self._clean_entry()
|
self._clean_entry()
|
||||||
self._is_empty = False
|
self._is_empty = False
|
||||||
@ -154,17 +169,25 @@ class Entry:
|
|||||||
raise TooLongNameException(
|
raise TooLongNameException(
|
||||||
'Maximal length of the object name is {} characters and {} characters for extension!'.format(
|
'Maximal length of the object name is {} characters and {} characters for extension!'.format(
|
||||||
MAX_NAME_SIZE, MAX_EXT_SIZE
|
MAX_NAME_SIZE, MAX_EXT_SIZE
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
start_address = self.entry_address
|
start_address = self.entry_address
|
||||||
end_address = start_address + self.fatfs_state.entry_size
|
end_address = start_address + self.fatfs_state.entry_size
|
||||||
if lfn_order in (self.SHORT_ENTRY, self.SHORT_ENTRY_LN):
|
if lfn_order in (self.SHORT_ENTRY, self.SHORT_ENTRY_LN):
|
||||||
|
date_entry_: int = build_date_entry(*date)
|
||||||
|
time_entry: int = build_time_entry(*time)
|
||||||
self.fatfs_state.binary_image[start_address: end_address] = self._build_entry(
|
self.fatfs_state.binary_image[start_address: end_address] = self._build_entry(
|
||||||
DIR_Name=pad_string(object_name, size=MAX_NAME_SIZE),
|
DIR_Name=pad_string(object_name, size=MAX_NAME_SIZE),
|
||||||
DIR_Name_ext=pad_string(object_extension, size=MAX_EXT_SIZE),
|
DIR_Name_ext=pad_string(object_extension, size=MAX_EXT_SIZE),
|
||||||
DIR_Attr=entity_type,
|
DIR_Attr=entity_type,
|
||||||
DIR_FstClusLO=first_cluster_id,
|
DIR_FstClusLO=first_cluster_id,
|
||||||
DIR_FileSize=size
|
DIR_FileSize=size,
|
||||||
|
DIR_CrtDate=date_entry_, # ignored by esp-idf fatfs library
|
||||||
|
DIR_LstAccDate=date_entry_, # must be same as DIR_WrtDate
|
||||||
|
DIR_WrtDate=date_entry_,
|
||||||
|
DIR_CrtTime=time_entry, # ignored by esp-idf fatfs library
|
||||||
|
DIR_WrtTime=time_entry
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert lfn_names is not None
|
assert lfn_names is not None
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
from construct import Const, Int8ul, Int16ul, Int32ul, PaddedString, Struct
|
from construct import Const, Int8ul, Int16ul, Int32ul, PaddedString, Struct
|
||||||
|
|
||||||
from .utils import (BYTES_PER_DIRECTORY_ENTRY, get_fatfs_type, get_non_data_sectors_cnt, number_of_clusters,
|
from .utils import (BYTES_PER_DIRECTORY_ENTRY, SHORT_NAMES_ENCODING, get_fatfs_type, get_non_data_sectors_cnt,
|
||||||
read_filesystem)
|
number_of_clusters, read_filesystem)
|
||||||
|
|
||||||
|
|
||||||
class FATFSParser:
|
class FATFSParser:
|
||||||
@ -16,7 +16,7 @@ class FATFSParser:
|
|||||||
|
|
||||||
BOOT_SECTOR_HEADER = Struct(
|
BOOT_SECTOR_HEADER = Struct(
|
||||||
'BS_jmpBoot' / Const(b'\xeb\xfe\x90'),
|
'BS_jmpBoot' / Const(b'\xeb\xfe\x90'),
|
||||||
'BS_OEMName' / PaddedString(MAX_OEM_NAME_SIZE, 'utf-8'),
|
'BS_OEMName' / PaddedString(MAX_OEM_NAME_SIZE, SHORT_NAMES_ENCODING),
|
||||||
'BPB_BytsPerSec' / Int16ul,
|
'BPB_BytsPerSec' / Int16ul,
|
||||||
'BPB_SecPerClus' / Int8ul,
|
'BPB_SecPerClus' / Int8ul,
|
||||||
'BPB_RsvdSecCnt' / Int16ul,
|
'BPB_RsvdSecCnt' / Int16ul,
|
||||||
@ -33,8 +33,8 @@ class FATFSParser:
|
|||||||
'BS_Reserved1' / Const(b'\x00'),
|
'BS_Reserved1' / Const(b'\x00'),
|
||||||
'BS_BootSig' / Const(b'\x29'),
|
'BS_BootSig' / Const(b'\x29'),
|
||||||
'BS_VolID' / Int32ul,
|
'BS_VolID' / Int32ul,
|
||||||
'BS_VolLab' / PaddedString(MAX_VOL_LAB_SIZE, 'utf-8'),
|
'BS_VolLab' / PaddedString(MAX_VOL_LAB_SIZE, SHORT_NAMES_ENCODING),
|
||||||
'BS_FilSysType' / PaddedString(MAX_FS_TYPE_SIZE, 'utf-8'),
|
'BS_FilSysType' / PaddedString(MAX_FS_TYPE_SIZE, SHORT_NAMES_ENCODING),
|
||||||
'BS_EMPTY' / Const(448 * b'\x00'),
|
'BS_EMPTY' / Const(448 * b'\x00'),
|
||||||
'Signature_word' / Const(b'\x55\xAA')
|
'Signature_word' / Const(b'\x55\xAA')
|
||||||
)
|
)
|
||||||
|
@ -29,6 +29,7 @@ class FATFSState:
|
|||||||
num_heads: int,
|
num_heads: int,
|
||||||
hidden_sectors: int,
|
hidden_sectors: int,
|
||||||
file_sys_type: str,
|
file_sys_type: str,
|
||||||
|
use_default_datetime: bool,
|
||||||
explicit_fat_type: int = None,
|
explicit_fat_type: int = None,
|
||||||
long_names_enabled: bool = False):
|
long_names_enabled: bool = False):
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ class FATFSState:
|
|||||||
self.size: int = size
|
self.size: int = size
|
||||||
self.sectors_per_fat_cnt: int = sectors_per_fat
|
self.sectors_per_fat_cnt: int = sectors_per_fat
|
||||||
self.sectors_per_cluster: int = sectors_per_cluster
|
self.sectors_per_cluster: int = sectors_per_cluster
|
||||||
|
self.use_default_datetime: bool = use_default_datetime
|
||||||
|
|
||||||
if self.clusters in (FAT12_MAX_CLUSTERS, FAT16_MAX_CLUSTERS):
|
if self.clusters in (FAT12_MAX_CLUSTERS, FAT16_MAX_CLUSTERS):
|
||||||
print('WARNING: It is not recommended to create FATFS with bounding '
|
print('WARNING: It is not recommended to create FATFS with bounding '
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime
|
||||||
from typing import List, Optional, Tuple, Union
|
from typing import List, Optional, Tuple, Union
|
||||||
|
|
||||||
from .entry import Entry
|
from .entry import Entry
|
||||||
@ -11,8 +12,8 @@ from .fatfs_state import FATFSState
|
|||||||
from .long_filename_utils import (build_lfn_full_name, build_lfn_unique_entry_name_order,
|
from .long_filename_utils import (build_lfn_full_name, build_lfn_unique_entry_name_order,
|
||||||
get_required_lfn_entries_count, split_name_to_lfn_entries,
|
get_required_lfn_entries_count, split_name_to_lfn_entries,
|
||||||
split_name_to_lfn_entry_blocks)
|
split_name_to_lfn_entry_blocks)
|
||||||
from .utils import (MAX_EXT_SIZE, MAX_NAME_SIZE, build_lfn_short_entry_name, lfn_checksum, required_clusters_count,
|
from .utils import (DATETIME, MAX_EXT_SIZE, MAX_NAME_SIZE, build_lfn_short_entry_name, lfn_checksum,
|
||||||
split_content_into_sectors, split_to_name_and_extension)
|
required_clusters_count, split_content_into_sectors, split_to_name_and_extension)
|
||||||
|
|
||||||
|
|
||||||
class File:
|
class File:
|
||||||
@ -180,8 +181,10 @@ class Directory:
|
|||||||
extension,
|
extension,
|
||||||
target_dir,
|
target_dir,
|
||||||
free_cluster,
|
free_cluster,
|
||||||
entity_type):
|
entity_type,
|
||||||
# type: (Entry, str, str, Directory, Cluster, int) -> Tuple[Cluster, Entry, Directory]
|
date,
|
||||||
|
time):
|
||||||
|
# type: (Entry, str, str, Directory, Cluster, int, DATETIME, DATETIME) -> Tuple[Cluster, Entry, Directory]
|
||||||
lfn_full_name: str = build_lfn_full_name(name, extension)
|
lfn_full_name: str = build_lfn_full_name(name, extension)
|
||||||
lfn_unique_entry_order: int = build_lfn_unique_entry_name_order(target_dir.entities, name)
|
lfn_unique_entry_order: int = build_lfn_unique_entry_name_order(target_dir.entities, name)
|
||||||
lfn_short_entry_name: str = build_lfn_short_entry_name(name, extension, lfn_unique_entry_order)
|
lfn_short_entry_name: str = build_lfn_short_entry_name(name, extension, lfn_unique_entry_order)
|
||||||
@ -207,15 +210,18 @@ class Directory:
|
|||||||
entity_name=lfn_short_entry_name[:MAX_NAME_SIZE],
|
entity_name=lfn_short_entry_name[:MAX_NAME_SIZE],
|
||||||
entity_extension=lfn_short_entry_name[MAX_NAME_SIZE:],
|
entity_extension=lfn_short_entry_name[MAX_NAME_SIZE:],
|
||||||
entity_type=entity_type,
|
entity_type=entity_type,
|
||||||
lfn_order=Entry.SHORT_ENTRY_LN)
|
lfn_order=Entry.SHORT_ENTRY_LN,
|
||||||
|
date=date,
|
||||||
|
time=time)
|
||||||
return free_cluster, free_entry, target_dir
|
return free_cluster, free_entry, target_dir
|
||||||
|
|
||||||
def allocate_object(self,
|
def allocate_object(self,
|
||||||
name,
|
name,
|
||||||
entity_type,
|
entity_type,
|
||||||
|
object_timestamp_,
|
||||||
path_from_root=None,
|
path_from_root=None,
|
||||||
extension=''):
|
extension=''):
|
||||||
# type: (str, int, Optional[List[str]], str) -> Tuple[Cluster, Entry, Directory]
|
# type: (str, int, datetime, Optional[List[str]], str) -> Tuple[Cluster, Entry, Directory]
|
||||||
"""
|
"""
|
||||||
Method finds the target directory in the path
|
Method finds the target directory in the path
|
||||||
and allocates cluster (both the record in FAT and cluster in the data region)
|
and allocates cluster (both the record in FAT and cluster in the data region)
|
||||||
@ -226,10 +232,16 @@ class Directory:
|
|||||||
free_entry: Entry = target_dir.find_free_entry() or target_dir.chain_directory()
|
free_entry: Entry = target_dir.find_free_entry() or target_dir.chain_directory()
|
||||||
|
|
||||||
name_fits_short_struct: bool = len(name) <= MAX_NAME_SIZE and len(extension) <= MAX_EXT_SIZE
|
name_fits_short_struct: bool = len(name) <= MAX_NAME_SIZE and len(extension) <= MAX_EXT_SIZE
|
||||||
|
|
||||||
|
fatfs_date_ = (object_timestamp_.year, object_timestamp_.month, object_timestamp_.day)
|
||||||
|
fatfs_time_ = (object_timestamp_.hour, object_timestamp_.minute, object_timestamp_.second)
|
||||||
|
|
||||||
if not self.fatfs_state.long_names_enabled or name_fits_short_struct:
|
if not self.fatfs_state.long_names_enabled or name_fits_short_struct:
|
||||||
free_entry.allocate_entry(first_cluster_id=free_cluster.id,
|
free_entry.allocate_entry(first_cluster_id=free_cluster.id,
|
||||||
entity_name=name,
|
entity_name=name,
|
||||||
entity_extension=extension,
|
entity_extension=extension,
|
||||||
|
date=fatfs_date_,
|
||||||
|
time=fatfs_time_,
|
||||||
entity_type=entity_type)
|
entity_type=entity_type)
|
||||||
return free_cluster, free_entry, target_dir
|
return free_cluster, free_entry, target_dir
|
||||||
return self.allocate_long_name_object(free_entry=free_entry,
|
return self.allocate_long_name_object(free_entry=free_entry,
|
||||||
@ -237,13 +249,20 @@ class Directory:
|
|||||||
extension=extension,
|
extension=extension,
|
||||||
target_dir=target_dir,
|
target_dir=target_dir,
|
||||||
free_cluster=free_cluster,
|
free_cluster=free_cluster,
|
||||||
entity_type=entity_type)
|
entity_type=entity_type,
|
||||||
|
date=fatfs_date_,
|
||||||
|
time=fatfs_time_)
|
||||||
|
|
||||||
def new_file(self, name: str, extension: str, path_from_root: Optional[List[str]]) -> None:
|
def new_file(self,
|
||||||
|
name: str,
|
||||||
|
extension: str,
|
||||||
|
path_from_root: Optional[List[str]],
|
||||||
|
object_timestamp_: datetime) -> None:
|
||||||
free_cluster, free_entry, target_dir = self.allocate_object(name=name,
|
free_cluster, free_entry, target_dir = self.allocate_object(name=name,
|
||||||
extension=extension,
|
extension=extension,
|
||||||
entity_type=Directory.ATTR_ARCHIVE,
|
entity_type=Directory.ATTR_ARCHIVE,
|
||||||
path_from_root=path_from_root)
|
path_from_root=path_from_root,
|
||||||
|
object_timestamp_=object_timestamp_)
|
||||||
|
|
||||||
file: File = File(name=name,
|
file: File = File(name=name,
|
||||||
fat=self.fat,
|
fat=self.fat,
|
||||||
@ -253,11 +272,12 @@ class Directory:
|
|||||||
file.first_cluster = free_cluster
|
file.first_cluster = free_cluster
|
||||||
target_dir.entities.append(file)
|
target_dir.entities.append(file)
|
||||||
|
|
||||||
def new_directory(self, name, parent, path_from_root):
|
def new_directory(self, name, parent, path_from_root, object_timestamp_):
|
||||||
# type: (str, Directory, Optional[List[str]]) -> None
|
# type: (str, Directory, Optional[List[str]], datetime) -> None
|
||||||
free_cluster, free_entry, target_dir = self.allocate_object(name=name,
|
free_cluster, free_entry, target_dir = self.allocate_object(name=name,
|
||||||
entity_type=Directory.ATTR_DIRECTORY,
|
entity_type=Directory.ATTR_DIRECTORY,
|
||||||
path_from_root=path_from_root)
|
path_from_root=path_from_root,
|
||||||
|
object_timestamp_=object_timestamp_)
|
||||||
|
|
||||||
directory: Directory = Directory(name=name,
|
directory: Directory = Directory(name=name,
|
||||||
fat=self.fat,
|
fat=self.fat,
|
||||||
|
@ -5,9 +5,10 @@ import argparse
|
|||||||
import binascii
|
import binascii
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
from construct import Int16ul
|
from construct import BitsInteger, BitStruct, Int16ul
|
||||||
|
|
||||||
FAT12_MAX_CLUSTERS: int = 4085
|
FAT12_MAX_CLUSTERS: int = 4085
|
||||||
FAT16_MAX_CLUSTERS: int = 65525
|
FAT16_MAX_CLUSTERS: int = 65525
|
||||||
@ -18,9 +19,12 @@ BYTES_PER_DIRECTORY_ENTRY: int = 32
|
|||||||
UINT32_MAX: int = (1 << 32) - 1
|
UINT32_MAX: int = (1 << 32) - 1
|
||||||
MAX_NAME_SIZE: int = 8
|
MAX_NAME_SIZE: int = 8
|
||||||
MAX_EXT_SIZE: int = 3
|
MAX_EXT_SIZE: int = 3
|
||||||
|
DATETIME = Tuple[int, int, int]
|
||||||
|
FATFS_INCEPTION: datetime = datetime(1980, 1, 1, 0, 0, 0, 0)
|
||||||
|
|
||||||
# long names are encoded to two bytes in utf-16
|
# long names are encoded to two bytes in utf-16
|
||||||
LONG_NAMES_ENCODING: str = 'utf-16'
|
LONG_NAMES_ENCODING: str = 'utf-16'
|
||||||
|
SHORT_NAMES_ENCODING: str = 'utf-8'
|
||||||
|
|
||||||
|
|
||||||
def crc32(input_values: List[int], crc: int) -> int:
|
def crc32(input_values: List[int], crc: int) -> int:
|
||||||
@ -99,7 +103,7 @@ def is_valid_fatfs_name(string: str) -> bool:
|
|||||||
return string == string.upper()
|
return string == string.upper()
|
||||||
|
|
||||||
|
|
||||||
def split_by_half_byte_12_bit_little_endian(value: int) -> Tuple[int, int, int]:
|
def split_by_half_byte_12_bit_little_endian(value: int) -> DATETIME:
|
||||||
value_as_bytes: bytes = Int16ul.build(value)
|
value_as_bytes: bytes = Int16ul.build(value)
|
||||||
return value_as_bytes[0] & 0x0f, value_as_bytes[0] >> 4, value_as_bytes[1] & 0x0f
|
return value_as_bytes[0] & 0x0f, value_as_bytes[0] >> 4, value_as_bytes[1] & 0x0f
|
||||||
|
|
||||||
@ -159,6 +163,10 @@ def get_args_for_partition_generator(desc: str) -> argparse.Namespace:
|
|||||||
parser.add_argument('--long_name_support',
|
parser.add_argument('--long_name_support',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Set flag to enable long names support.')
|
help='Set flag to enable long names support.')
|
||||||
|
parser.add_argument('--use_default_datetime',
|
||||||
|
action='store_true',
|
||||||
|
help='For test purposes. If the flag is set the files are created with '
|
||||||
|
'the default timestamp that is the 1st of January 1980')
|
||||||
parser.add_argument('--fat_type',
|
parser.add_argument('--fat_type',
|
||||||
default=0,
|
default=0,
|
||||||
type=int,
|
type=int,
|
||||||
@ -180,3 +188,45 @@ def get_args_for_partition_generator(desc: str) -> argparse.Namespace:
|
|||||||
def read_filesystem(path: str) -> bytearray:
|
def read_filesystem(path: str) -> bytearray:
|
||||||
with open(path, 'rb') as fs_file:
|
with open(path, 'rb') as fs_file:
|
||||||
return bytearray(fs_file.read())
|
return bytearray(fs_file.read())
|
||||||
|
|
||||||
|
|
||||||
|
DATE_ENTRY = BitStruct(
|
||||||
|
'year' / BitsInteger(7),
|
||||||
|
'month' / BitsInteger(4),
|
||||||
|
'day' / BitsInteger(5))
|
||||||
|
|
||||||
|
TIME_ENTRY = BitStruct(
|
||||||
|
'hour' / BitsInteger(5),
|
||||||
|
'minute' / BitsInteger(6),
|
||||||
|
'second' / BitsInteger(5),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_date_entry(year: int, mon: int, mday: int) -> int:
|
||||||
|
"""
|
||||||
|
:param year: denotes year starting from 1980 (0 ~ 1980, 1 ~ 1981, etc), valid values are 1980 + 0..127 inclusive
|
||||||
|
thus theoretically 1980 - 2107
|
||||||
|
:param mon: denotes number of month of year in common order (1 ~ January, 2 ~ February, etc.),
|
||||||
|
valid values: 1..12 inclusive
|
||||||
|
:param mday: denotes number of day in month, valid values are 1..31 inclusive
|
||||||
|
|
||||||
|
:returns: 16 bit integer number (7 bits for year, 4 bits for month and 5 bits for day of the month)
|
||||||
|
"""
|
||||||
|
assert year in range(1980, 2107)
|
||||||
|
assert mon in range(1, 13)
|
||||||
|
assert mday in range(1, 32)
|
||||||
|
return int.from_bytes(DATE_ENTRY.build(dict(year=year - 1980, month=mon, day=mday)), 'big')
|
||||||
|
|
||||||
|
|
||||||
|
def build_time_entry(hour: int, minute: int, sec: int) -> int:
|
||||||
|
"""
|
||||||
|
:param hour: denotes number of hour, valid values are 0..23 inclusive
|
||||||
|
:param minute: denotes minutes, valid range 0..59 inclusive
|
||||||
|
:param sec: denotes seconds with granularity 2 seconds (e.g. 1 ~ 2, 29 ~ 58), valid range 0..29 inclusive
|
||||||
|
|
||||||
|
:returns: 16 bit integer number (5 bits for hour, 6 bits for minute and 5 bits for second)
|
||||||
|
"""
|
||||||
|
assert hour in range(0, 23)
|
||||||
|
assert minute in range(0, 60)
|
||||||
|
assert sec in range(0, 60)
|
||||||
|
return int.from_bytes(TIME_ENTRY.build(dict(hour=hour, minute=minute, second=sec // 2)), 'big')
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# Create a fatfs image of the specified directory on the host during build and optionally
|
# Create a fatfs image of the specified directory on the host during build and optionally
|
||||||
# have the created image flashed using `idf.py flash`
|
# have the created image flashed using `idf.py flash`
|
||||||
function(fatfs_create_partition_image partition base_dir)
|
function(fatfs_create_partition_image partition base_dir)
|
||||||
set(options FLASH_IN_PROJECT WL_INIT)
|
set(options FLASH_IN_PROJECT WL_INIT PRESERVE_TIME)
|
||||||
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
||||||
|
|
||||||
|
|
||||||
@ -16,6 +16,12 @@ function(fatfs_create_partition_image partition base_dir)
|
|||||||
set(fatfsgen_py ${python} ${idf_path}/components/fatfs/fatfsgen.py)
|
set(fatfsgen_py ${python} ${idf_path}/components/fatfs/fatfsgen.py)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(arg_PRESERVE_TIME)
|
||||||
|
set(default_datetime_option)
|
||||||
|
else()
|
||||||
|
set(default_datetime_option --use_default_datetime)
|
||||||
|
endif()
|
||||||
|
|
||||||
if("${CONFIG_FATFS_SECTORS_PER_CLUSTER_1}")
|
if("${CONFIG_FATFS_SECTORS_PER_CLUSTER_1}")
|
||||||
set(sectors_per_cluster 1)
|
set(sectors_per_cluster 1)
|
||||||
elseif("${CONFIG_FATFS_SECTORS_PER_CLUSTER_2}")
|
elseif("${CONFIG_FATFS_SECTORS_PER_CLUSTER_2}")
|
||||||
@ -60,6 +66,8 @@ function(fatfs_create_partition_image partition base_dir)
|
|||||||
set(fatfs_explicit_type 16)
|
set(fatfs_explicit_type 16)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
get_filename_component(base_dir_full_path ${base_dir} ABSOLUTE)
|
get_filename_component(base_dir_full_path ${base_dir} ABSOLUTE)
|
||||||
partition_table_get_partition_info(size "--partition-name ${partition}" "size")
|
partition_table_get_partition_info(size "--partition-name ${partition}" "size")
|
||||||
partition_table_get_partition_info(offset "--partition-name ${partition}" "offset")
|
partition_table_get_partition_info(offset "--partition-name ${partition}" "offset")
|
||||||
@ -71,6 +79,7 @@ function(fatfs_create_partition_image partition base_dir)
|
|||||||
add_custom_target(fatfs_${partition}_bin ALL
|
add_custom_target(fatfs_${partition}_bin ALL
|
||||||
COMMAND ${fatfsgen_py} ${base_dir_full_path}
|
COMMAND ${fatfsgen_py} ${base_dir_full_path}
|
||||||
${fatfs_long_names_option}
|
${fatfs_long_names_option}
|
||||||
|
${default_datetime_option}
|
||||||
--partition_size ${size}
|
--partition_size ${size}
|
||||||
--output_file ${image_file}
|
--output_file ${image_file}
|
||||||
--sector_size "${fatfs_sector_size}"
|
--sector_size "${fatfs_sector_size}"
|
||||||
@ -102,21 +111,39 @@ endfunction()
|
|||||||
|
|
||||||
|
|
||||||
function(fatfs_create_rawflash_image partition base_dir)
|
function(fatfs_create_rawflash_image partition base_dir)
|
||||||
set(options FLASH_IN_PROJECT)
|
set(options FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
||||||
|
|
||||||
if(arg_FLASH_IN_PROJECT)
|
if(arg_FLASH_IN_PROJECT)
|
||||||
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT)
|
if(arg_PRESERVE_TIME)
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
|
else()
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
fatfs_create_partition_image(${partition} ${base_dir})
|
if(arg_PRESERVE_TIME)
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} PRESERVE_TIME)
|
||||||
|
else()
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(fatfs_create_spiflash_image partition base_dir)
|
function(fatfs_create_spiflash_image partition base_dir)
|
||||||
set(options FLASH_IN_PROJECT)
|
set(options FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
|
||||||
|
|
||||||
if(arg_FLASH_IN_PROJECT)
|
if(arg_FLASH_IN_PROJECT)
|
||||||
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT WL_INIT)
|
if(arg_PRESERVE_TIME)
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT WL_INIT PRESERVE_TIME)
|
||||||
|
else()
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} FLASH_IN_PROJECT WL_INIT)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
fatfs_create_partition_image(${partition} ${base_dir} WL_INIT)
|
if(arg_PRESERVE_TIME)
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} WL_INIT PRESERVE_TIME)
|
||||||
|
else()
|
||||||
|
fatfs_create_partition_image(${partition} ${base_dir} WL_INIT)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
@ -217,7 +217,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
file_system = read_filesystem(CFG['output_file'])
|
file_system = read_filesystem(CFG['output_file'])
|
||||||
|
|
||||||
self.assertEqual(file_system[0x2000:0x2010], b'TESTFOLD \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2000:0x2010], b'TESTFOLD \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2010:0x2020], b'!\x00\x00\x00\x00\x00\x00\x00\x21\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2010:0x2020], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040:0x6050], b'TESTFOLD \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6040:0x6050], b'TESTFOLD \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040:0x6050], b'TESTFOLD \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6040:0x6050], b'TESTFOLD \x10\x00\x00\x00\x00')
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
fatfs.write_filesystem(CFG['output_file'])
|
fatfs.write_filesystem(CFG['output_file'])
|
||||||
file_system = read_filesystem(CFG['output_file'])
|
file_system = read_filesystem(CFG['output_file'])
|
||||||
self.assertEqual(file_system[0x6060:0x6070], b'TESTFIL2 \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6060:0x6070], b'TESTFIL2 \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6070:0x6080], b'!\x00\x00\x00\x00\x00\x00\x00\x21\x00\x05\x00\x0b\x00\x00\x00')
|
self.assertEqual(file_system[0x6070:0x6080], b'!\x00!\x00\x00\x00\x00\x00!\x00\x05\x00\x0b\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x7040:0x7050], b'LASTFILE \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x7040:0x7050], b'LASTFILE \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x8000:0x8010], b'deeptest\n\x00\x00\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x8000:0x8010], b'deeptest\n\x00\x00\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x9000:0x9010], b'thisistest\n\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x9000:0x9010], b'thisistest\n\x00\x00\x00\x00\x00')
|
||||||
@ -294,7 +294,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
fatfs.create_file('HELLO', extension='TXT')
|
fatfs.create_file('HELLO', extension='TXT')
|
||||||
fatfs.write_filesystem(CFG['output_file'])
|
fatfs.write_filesystem(CFG['output_file'])
|
||||||
file_system = read_filesystem(CFG['output_file'])
|
file_system = read_filesystem(CFG['output_file'])
|
||||||
self.assertEqual(file_system[0x2000: 0x2019], b'HELLO TXT \x00\x00\x00\x00!\x00\x00\x00\x00\x00\x00\x00!')
|
self.assertEqual(file_system[0x2000: 0x2019], b'HELLO TXT \x00\x00\x00\x00!\x00!\x00\x00\x00\x00\x00!')
|
||||||
|
|
||||||
def test_lfn_short_name(self) -> None:
|
def test_lfn_short_name(self) -> None:
|
||||||
fatfs = fatfsgen.FATFS(long_names_enabled=True)
|
fatfs = fatfsgen.FATFS(long_names_enabled=True)
|
||||||
@ -303,7 +303,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
fatfs.write_filesystem(CFG['output_file'])
|
fatfs.write_filesystem(CFG['output_file'])
|
||||||
file_system = read_filesystem(CFG['output_file'])
|
file_system = read_filesystem(CFG['output_file'])
|
||||||
self.assertEqual(file_system[0x2000: 0x2010], b'HELLO TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2000: 0x2010], b'HELLO TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2010: 0x2020], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
self.assertEqual(file_system[0x2010: 0x2020], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
||||||
|
|
||||||
def test_lfn_empty_name(self) -> None:
|
def test_lfn_empty_name(self) -> None:
|
||||||
@ -316,7 +316,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
def test_lfn_plain_name(self) -> None:
|
def test_lfn_plain_name(self) -> None:
|
||||||
fatfs = fatfsgen.FATFS(long_names_enabled=True)
|
fatfs = fatfsgen.FATFS(long_names_enabled=True)
|
||||||
@ -329,7 +329,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
||||||
|
|
||||||
def test_lfn_plain_name_no_ext(self) -> None:
|
def test_lfn_plain_name_no_ext(self) -> None:
|
||||||
@ -343,7 +343,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00Vh\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00Vh\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1 \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'HELLOH~1 \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x0e\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'this is a test\x00\x00')
|
||||||
|
|
||||||
def test_lfn_short_entry_exception(self) -> None:
|
def test_lfn_short_entry_exception(self) -> None:
|
||||||
@ -361,14 +361,14 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6012: 0x6020], b'\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6012: 0x6020], b'!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00!\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040: 0x6050], b'HELLO TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6040: 0x6050], b'HELLO TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6050: 0x6060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x03\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6050: 0x6060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x03\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
def test_lfn_nested_long_empty(self) -> None:
|
def test_lfn_nested_long_empty(self) -> None:
|
||||||
fatfs: fatfsgen.FATFS = fatfsgen.FATFS(long_names_enabled=True)
|
fatfs: fatfsgen.FATFS = fatfsgen.FATFS(long_names_enabled=True)
|
||||||
@ -382,12 +382,12 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\x10o\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\x10o\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'verylo~1 \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'verylo~1 \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6012: 0x6020], b'\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6012: 0x6020], b'!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00!\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040: 0x6050], b'Bl\x00o\x00.\x00t\x00x\x00\x0f\x00\xd5t\x00')
|
self.assertEqual(file_system[0x6040: 0x6050], b'Bl\x00o\x00.\x00t\x00x\x00\x0f\x00\xd5t\x00')
|
||||||
self.assertEqual(file_system[0x6050: 0x6060],
|
self.assertEqual(file_system[0x6050: 0x6060],
|
||||||
b'\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff')
|
b'\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff')
|
||||||
@ -404,14 +404,14 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6012: 0x6020], b'\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6012: 0x6020], b'!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00!\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040: 0x6050], b'HELLO TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6040: 0x6050], b'HELLO TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6050: 0x6060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x03\x00\x0e\x00\x00\x00')
|
self.assertEqual(file_system[0x6050: 0x6060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x03\x00\x0e\x00\x00\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x7000: 0x7010], b'this is a test\x00\x00')
|
self.assertEqual(file_system[0x7000: 0x7010], b'this is a test\x00\x00')
|
||||||
|
|
||||||
@ -452,12 +452,12 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
self.assertEqual(file_system[0x2020: 0x2030], b'\x01v\x00e\x00r\x00y\x00l\x00\x0f\x00\xa6o\x00')
|
||||||
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
self.assertEqual(file_system[0x2030: 0x2040], b'n\x00g\x00t\x00e\x00s\x00\x00\x00t\x00f\x00')
|
||||||
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2040: 0x2050], b'VERYLO~1 \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x2050: 0x2060], b'!\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6000: 0x6010], b'. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6011: 0x6020], b'\x00\x00\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6011: 0x6020], b'\x00!\x00\x00\x00\x00\x00!\x00\x02\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6020: 0x6030], b'.. \x10\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6030: 0x6040], b'!\x00!\x00\x00\x00\x00\x00!\x00\x01\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6040: 0x6050], b'Bl\x00o\x00o\x00o\x00o\x00\x0f\x00\xb3o\x00')
|
self.assertEqual(file_system[0x6040: 0x6050], b'Bl\x00o\x00o\x00o\x00o\x00\x0f\x00\xb3o\x00')
|
||||||
self.assertEqual(file_system[0x6050: 0x6060], b'o\x00o\x00.\x00t\x00x\x00\x00\x00t\x00\x00\x00')
|
self.assertEqual(file_system[0x6050: 0x6060], b'o\x00o\x00.\x00t\x00x\x00\x00\x00t\x00\x00\x00')
|
||||||
|
|
||||||
@ -465,7 +465,7 @@ class FatFSGen(unittest.TestCase):
|
|||||||
self.assertEqual(file_system[0x6060: 0x6070], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
self.assertEqual(file_system[0x6060: 0x6070], b'\x01h\x00e\x00l\x00l\x00o\x00\x0f\x00\xb3h\x00')
|
||||||
self.assertEqual(file_system[0x6070: 0x6080], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
self.assertEqual(file_system[0x6070: 0x6080], b'e\x00l\x00l\x00o\x00h\x00\x00\x00e\x00l\x00')
|
||||||
self.assertEqual(file_system[0x6080: 0x6090], b'HELLOH~1TXT \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x6080: 0x6090], b'HELLOH~1TXT \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x6090: 0x60a0], b'!\x00\x00\x00\x00\x00\x00\x00!\x00\x03\x00\x10\x00\x00\x00')
|
self.assertEqual(file_system[0x6090: 0x60a0], b'!\x00!\x00\x00\x00\x00\x00!\x00\x03\x00\x10\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x60a0: 0x60b0], b'Bl\x00o\x00o\x00o\x00o\x00\x0f\x00\x93o\x00')
|
self.assertEqual(file_system[0x60a0: 0x60b0], b'Bl\x00o\x00o\x00o\x00o\x00\x0f\x00\x93o\x00')
|
||||||
|
|
||||||
self.assertEqual(file_system[0x60b0: 0x60c0], b'o\x00b\x00.\x00t\x00x\x00\x00\x00t\x00\x00\x00')
|
self.assertEqual(file_system[0x60b0: 0x60c0], b'o\x00b\x00.\x00t\x00x\x00\x00\x00t\x00\x00\x00')
|
||||||
|
@ -134,7 +134,7 @@ class WLFatFSGen(unittest.TestCase):
|
|||||||
file_system = bytearray(fs_file.read())
|
file_system = bytearray(fs_file.read())
|
||||||
|
|
||||||
self.assertEqual(file_system[0x7060:0x7070], b'TESTFIL2 \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x7060:0x7070], b'TESTFIL2 \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x7070:0x7080], b'!\x00\x00\x00\x00\x00\x00\x00\x21\x00\x05\x00\x0b\x00\x00\x00')
|
self.assertEqual(file_system[0x7070:0x7080], b'!\x00!\x00\x00\x00\x00\x00!\x00\x05\x00\x0b\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x8040:0x8050], b'LASTFILE \x00\x00\x00\x00')
|
self.assertEqual(file_system[0x8040:0x8050], b'LASTFILE \x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0x9000:0x9010], b'deeptest\n\x00\x00\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0x9000:0x9010], b'deeptest\n\x00\x00\x00\x00\x00\x00\x00')
|
||||||
self.assertEqual(file_system[0xa000:0xa010], b'thisistest\n\x00\x00\x00\x00\x00')
|
self.assertEqual(file_system[0xa000:0xa010], b'thisistest\n\x00\x00\x00\x00\x00')
|
||||||
|
@ -60,6 +60,7 @@ class WLFATFS:
|
|||||||
sec_per_track: int = 0x3f,
|
sec_per_track: int = 0x3f,
|
||||||
volume_label: str = 'Espressif',
|
volume_label: str = 'Espressif',
|
||||||
file_sys_type: str = 'FAT',
|
file_sys_type: str = 'FAT',
|
||||||
|
use_default_datetime: bool = True,
|
||||||
version: int = 2,
|
version: int = 2,
|
||||||
temp_buff_size: int = 32,
|
temp_buff_size: int = 32,
|
||||||
update_rate: int = 16,
|
update_rate: int = 16,
|
||||||
@ -101,6 +102,7 @@ class WLFATFS:
|
|||||||
long_names_enabled=long_names_enabled,
|
long_names_enabled=long_names_enabled,
|
||||||
entry_size=entry_size,
|
entry_size=entry_size,
|
||||||
num_heads=num_heads,
|
num_heads=num_heads,
|
||||||
|
use_default_datetime=use_default_datetime,
|
||||||
oem_name=oem_name,
|
oem_name=oem_name,
|
||||||
sec_per_track=sec_per_track,
|
sec_per_track=sec_per_track,
|
||||||
volume_label=volume_label,
|
volume_label=volume_label,
|
||||||
@ -194,7 +196,8 @@ if __name__ == '__main__':
|
|||||||
size=args.partition_size,
|
size=args.partition_size,
|
||||||
root_entry_count=args.root_entry_count,
|
root_entry_count=args.root_entry_count,
|
||||||
explicit_fat_type=args.fat_type,
|
explicit_fat_type=args.fat_type,
|
||||||
long_names_enabled=args.long_name_support)
|
long_names_enabled=args.long_name_support,
|
||||||
|
use_default_datetime=args.use_default_datetime)
|
||||||
|
|
||||||
wl_fatfs.wl_generate(args.input_directory)
|
wl_fatfs.wl_generate(args.input_directory)
|
||||||
wl_fatfs.init_wl()
|
wl_fatfs.init_wl()
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import ttfw_idf
|
import ttfw_idf
|
||||||
@ -7,53 +10,85 @@ import ttfw_idf
|
|||||||
|
|
||||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||||
def test_examples_fatfsgen(env: ttfw_idf.TinyFW.Env, _: Optional[list]) -> None:
|
def test_examples_fatfsgen(env: ttfw_idf.TinyFW.Env, _: Optional[list]) -> None:
|
||||||
dut = env.get_dut('fatfsgen', 'examples/storage/fatfsgen', app_config_name='test_read_write_partition_gen')
|
tag = 'fatfsgen'
|
||||||
dut.start_app()
|
test_path = 'examples/storage/fatfsgen'
|
||||||
dut.expect_all('example: Mounting FAT filesystem',
|
timeout = 20
|
||||||
'example: Opening file',
|
filename_ln = 'sublongnames/testlongfilenames.txt'
|
||||||
'example: File written',
|
filename_sn = 'sub/test.txt'
|
||||||
'example: Reading file',
|
dir_ln = os.path.join(os.path.dirname(__file__), 'fatfs_long_name_image')
|
||||||
'example: Read from file: \'This is written by the device\'',
|
dir_sn = os.path.join(os.path.dirname(__file__), 'fatfs_image')
|
||||||
'example: Reading file',
|
Path(os.path.join(dir_ln, filename_ln)).touch()
|
||||||
'example: Read from file: \'This is generated on the host\'',
|
Path(os.path.join(dir_sn, filename_sn)).touch()
|
||||||
'example: Unmounting FAT filesystem',
|
date_modified = datetime.today().strftime('%Y-%m-%d')
|
||||||
'example: Done',
|
date_default = '1980-01-01'
|
||||||
timeout=20)
|
|
||||||
env.close_dut(dut.name)
|
|
||||||
|
|
||||||
dut = env.get_dut('fatfsgen', 'examples/storage/fatfsgen', app_config_name='test_read_only_partition_gen')
|
for test_name in ['test_read_write_partition_gen', 'test_read_write_partition_gen_default_dt']:
|
||||||
dut.start_app()
|
filename = filename_sn
|
||||||
dut.expect_all('example: Mounting FAT filesystem',
|
filename_expected = f'/spiflash/{filename}'
|
||||||
'example: Reading file',
|
date_ = date_default if test_name == 'test_read_write_partition_gen_default_dt' else date_modified
|
||||||
'example: Read from file: \'this is test\'',
|
dut = env.get_dut(tag, test_path, app_config_name=test_name)
|
||||||
'example: Unmounting FAT filesystem',
|
dut.start_app()
|
||||||
'example: Done',
|
dut.expect_all('example: Mounting FAT filesystem',
|
||||||
timeout=20)
|
'example: Opening file',
|
||||||
env.close_dut(dut.name)
|
'example: File written',
|
||||||
|
'example: Reading file',
|
||||||
|
'example: Read from file: \'This is written by the device\'',
|
||||||
|
'example: Reading file',
|
||||||
|
f'The file \'{filename_expected}\' was modified at date: {date_}',
|
||||||
|
'example: Read from file: \'This is generated on the host\'',
|
||||||
|
'example: Unmounting FAT filesystem',
|
||||||
|
'example: Done',
|
||||||
|
timeout=timeout)
|
||||||
|
env.close_dut(dut.name)
|
||||||
|
|
||||||
dut = env.get_dut('fatfsgen', 'examples/storage/fatfsgen', app_config_name='test_read_write_partition_gen_ln')
|
for test_name in ['test_read_only_partition_gen', 'test_read_only_partition_gen_default_dt']:
|
||||||
dut.start_app()
|
filename = filename_sn
|
||||||
dut.expect_all('example: Mounting FAT filesystem',
|
filename_expected = f'/spiflash/{filename}'
|
||||||
'example: Opening file',
|
date_ = date_default if test_name == 'test_read_only_partition_gen_default_dt' else date_modified
|
||||||
'example: File written',
|
dut = env.get_dut(tag, test_path, app_config_name=test_name)
|
||||||
'example: Reading file',
|
dut.start_app()
|
||||||
'example: Read from file: \'This is written by the device\'',
|
dut.expect_all('example: Mounting FAT filesystem',
|
||||||
'example: Reading file',
|
'example: Reading file',
|
||||||
'example: Read from file: \'This is generated on the host; long name it has\'',
|
f'The file \'{filename_expected}\' was modified at date: {date_}',
|
||||||
'example: Unmounting FAT filesystem',
|
'example: Read from file: \'this is test\'',
|
||||||
'example: Done',
|
'example: Unmounting FAT filesystem',
|
||||||
timeout=20)
|
'example: Done',
|
||||||
env.close_dut(dut.name)
|
timeout=timeout)
|
||||||
|
env.close_dut(dut.name)
|
||||||
|
|
||||||
dut = env.get_dut('fatfsgen', 'examples/storage/fatfsgen', app_config_name='test_read_only_partition_gen_ln')
|
for test_name in ['test_read_write_partition_gen_ln', 'test_read_write_partition_gen_ln_default_dt']:
|
||||||
dut.start_app()
|
filename = filename_ln
|
||||||
dut.expect_all('example: Mounting FAT filesystem',
|
filename_expected = f'/spiflash/{filename}'
|
||||||
'example: Reading file',
|
date_ = date_default if test_name == 'test_read_write_partition_gen_ln_default_dt' else date_modified
|
||||||
'example: Read from file: \'this is test; long name it has\'',
|
dut = env.get_dut(tag, test_path, app_config_name=test_name)
|
||||||
'example: Unmounting FAT filesystem',
|
dut.start_app()
|
||||||
'example: Done',
|
dut.expect_all('example: Mounting FAT filesystem',
|
||||||
timeout=20)
|
'example: Opening file',
|
||||||
env.close_dut(dut.name)
|
'example: File written',
|
||||||
|
'example: Reading file',
|
||||||
|
'example: Read from file: \'This is written by the device\'',
|
||||||
|
'example: Reading file',
|
||||||
|
f'The file \'{filename_expected}\' was modified at date: {date_}',
|
||||||
|
'example: Read from file: \'This is generated on the host; long name it has\'',
|
||||||
|
'example: Unmounting FAT filesystem',
|
||||||
|
'example: Done',
|
||||||
|
timeout=timeout)
|
||||||
|
env.close_dut(dut.name)
|
||||||
|
|
||||||
|
for test_name in ['test_read_only_partition_gen_ln', 'test_read_only_partition_gen_ln_default_dt']:
|
||||||
|
filename = filename_ln
|
||||||
|
filename_expected = f'/spiflash/{filename}'
|
||||||
|
date_ = date_default if test_name == 'test_read_only_partition_gen_ln_default_dt' else date_modified
|
||||||
|
dut = env.get_dut(tag, test_path, app_config_name=test_name)
|
||||||
|
dut.start_app()
|
||||||
|
dut.expect_all('example: Mounting FAT filesystem',
|
||||||
|
'example: Reading file',
|
||||||
|
f'The file \'{filename_expected}\' was modified at date: {date_}',
|
||||||
|
'example: Read from file: \'this is test; long name it has\'',
|
||||||
|
'example: Unmounting FAT filesystem',
|
||||||
|
'example: Done',
|
||||||
|
timeout=timeout)
|
||||||
|
env.close_dut(dut.name)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -16,7 +16,15 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY)
|
if(CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY)
|
||||||
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT)
|
if(CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME)
|
||||||
|
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT)
|
||||||
|
else()
|
||||||
|
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT)
|
if(CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME)
|
||||||
|
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT)
|
||||||
|
else()
|
||||||
|
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -7,4 +7,11 @@ menu "Example Configuration"
|
|||||||
If read-only mode is set, the generated fatfs image will be raw (without wear levelling support).
|
If read-only mode is set, the generated fatfs image will be raw (without wear levelling support).
|
||||||
Otherwise it will support wear levelling that enables read-write mounting.
|
Otherwise it will support wear levelling that enables read-write mounting.
|
||||||
|
|
||||||
|
config EXAMPLE_FATFS_DEFAULT_DATETIME
|
||||||
|
bool "Default modification date and time for generated FATFS image"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
If default datetime is set, all files created in the generated FATFS partition have default time
|
||||||
|
equal to FATFS origin time (1 January 1980)
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -106,6 +106,20 @@ void app_main(void)
|
|||||||
host_filename2 = "/spiflash/hello.txt";
|
host_filename2 = "/spiflash/hello.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct stat info;
|
||||||
|
struct tm timeinfo;
|
||||||
|
char buffer[32];
|
||||||
|
|
||||||
|
if(stat(host_filename1, &info) < 0){
|
||||||
|
ESP_LOGE(TAG, "Failed to read file stats");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localtime_r(&info.st_mtime, &timeinfo);
|
||||||
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "The file '%s' was modified at date: %s", host_filename1, buffer);
|
||||||
|
|
||||||
|
|
||||||
if (EXAMPLE_FATFS_MODE_READ_ONLY){
|
if (EXAMPLE_FATFS_MODE_READ_ONLY){
|
||||||
f = fopen(host_filename1, "rb");
|
f = fopen(host_filename1, "rb");
|
||||||
} else {
|
} else {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=y
|
||||||
|
CONFIG_FATFS_LFN_HEAP=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
||||||
|
CONFIG_FATFS_LFN_NONE=y
|
||||||
|
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=y
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
||||||
|
CONFIG_FATFS_LFN_NONE=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
||||||
|
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=n
|
||||||
|
CONFIG_FATFS_LFN_HEAP=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
||||||
|
CONFIG_FATFS_LFN_NONE=y
|
||||||
|
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=n
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
||||||
|
CONFIG_FATFS_LFN_NONE=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
||||||
|
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
Loading…
Reference in New Issue
Block a user