esp-idf/components/fatfs/fatfs_utils/fatfs_state.py
2022-08-01 20:29:02 +02:00

164 lines
6.8 KiB
Python

# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from textwrap import dedent
from typing import Optional
from .exceptions import InconsistentFATAttributes
from .utils import (ALLOWED_SECTOR_SIZES, FAT12, FAT12_MAX_CLUSTERS, FAT16, FAT16_MAX_CLUSTERS, FATDefaults,
get_fat_sectors_count, get_fatfs_type, get_non_data_sectors_cnt, number_of_clusters)
class FATFSState:
"""
The class represents the state and the configuration of the FATFS.
"""
def __init__(self,
sector_size: int,
reserved_sectors_cnt: int,
root_dir_sectors_cnt: int,
size: int,
media_type: int,
sectors_per_cluster: int,
volume_label: str,
oem_name: str,
fat_tables_cnt: int,
sec_per_track: int,
num_heads: int,
hidden_sectors: int,
file_sys_type: str,
use_default_datetime: bool,
explicit_fat_type: Optional[int] = None,
long_names_enabled: bool = False):
self.boot_sector_state = BootSectorState(oem_name=oem_name,
sector_size=sector_size,
sectors_per_cluster=sectors_per_cluster,
reserved_sectors_cnt=reserved_sectors_cnt,
fat_tables_cnt=fat_tables_cnt,
root_dir_sectors_cnt=root_dir_sectors_cnt,
sectors_count=size // sector_size,
media_type=media_type,
sec_per_track=sec_per_track,
num_heads=num_heads,
hidden_sectors=hidden_sectors,
volume_label=volume_label,
file_sys_type=file_sys_type,
volume_uuid=-1)
self._explicit_fat_type: Optional[int] = explicit_fat_type
self.long_names_enabled: bool = long_names_enabled
self.use_default_datetime: bool = use_default_datetime
if (size // sector_size) * sectors_per_cluster in (FAT12_MAX_CLUSTERS, FAT16_MAX_CLUSTERS):
print('WARNING: It is not recommended to create FATFS with bounding '
f'count of clusters: {FAT12_MAX_CLUSTERS} or {FAT16_MAX_CLUSTERS}')
self.check_fat_type()
@property
def binary_image(self) -> bytearray:
return self.boot_sector_state.binary_image
@binary_image.setter
def binary_image(self, value: bytearray) -> None:
self.boot_sector_state.binary_image = value
def check_fat_type(self) -> None:
_type = self.boot_sector_state.fatfs_type
if self._explicit_fat_type is not None and self._explicit_fat_type != _type:
raise InconsistentFATAttributes(dedent(
f"""FAT type you specified is inconsistent with other attributes of the system.
The specified FATFS type: FAT{self._explicit_fat_type}
The actual FATFS type: FAT{_type}"""))
if _type not in (FAT12, FAT16):
raise NotImplementedError('FAT32 is currently not supported.')
class BootSectorState:
# pylint: disable=too-many-instance-attributes
def __init__(self,
oem_name: str,
sector_size: int,
sectors_per_cluster: int,
reserved_sectors_cnt: int,
fat_tables_cnt: int,
root_dir_sectors_cnt: int,
sectors_count: int,
media_type: int,
sec_per_track: int,
num_heads: int,
hidden_sectors: int,
volume_label: str,
file_sys_type: str,
volume_uuid: int = -1) -> None:
self.oem_name: str = oem_name
self.sector_size: int = sector_size
assert self.sector_size in ALLOWED_SECTOR_SIZES
self.sectors_per_cluster: int = sectors_per_cluster
self.reserved_sectors_cnt: int = reserved_sectors_cnt
self.fat_tables_cnt: int = fat_tables_cnt
self.root_dir_sectors_cnt: int = root_dir_sectors_cnt
self.sectors_count: int = sectors_count
self.media_type: int = media_type
self.sectors_per_fat_cnt = get_fat_sectors_count(self.size // self.sector_size, self.sector_size)
self.sec_per_track: int = sec_per_track
self.num_heads: int = num_heads
self.hidden_sectors: int = hidden_sectors
self.volume_label: str = volume_label
self.file_sys_type: str = file_sys_type
self.volume_uuid: int = volume_uuid
self._binary_image: bytearray = bytearray(b'')
@property
def binary_image(self) -> bytearray:
return self._binary_image
@binary_image.setter
def binary_image(self, value: bytearray) -> None:
self._binary_image = value
@property
def size(self) -> int:
return self.sector_size * self.sectors_count
@property
def data_region_start(self) -> int:
return self.non_data_sectors * self.sector_size
@property
def fatfs_type(self) -> int:
# variable typed_fatfs_type must be explicitly typed to avoid mypy error
typed_fatfs_type: int = get_fatfs_type(self.clusters)
return typed_fatfs_type
@property
def clusters(self) -> int:
clusters_cnt_: int = number_of_clusters(self.data_sectors, self.sectors_per_cluster)
return clusters_cnt_
@property
def data_sectors(self) -> int:
# self.sector_size is checked in constructor if has one of allowed values (ALLOWED_SECTOR_SIZES)
return (self.size // self.sector_size) - self.non_data_sectors
@property
def non_data_sectors(self) -> int:
non_data_sectors_: int = get_non_data_sectors_cnt(self.reserved_sectors_cnt,
self.sectors_per_fat_cnt,
self.root_dir_sectors_cnt)
return non_data_sectors_
@property
def fat_table_start_address(self) -> int:
return self.sector_size * self.reserved_sectors_cnt
@property
def entries_root_count(self) -> int:
entries_root_count_: int = (self.root_dir_sectors_cnt * self.sector_size) // FATDefaults.ENTRY_SIZE
return entries_root_count_
@property
def root_directory_start(self) -> int:
root_dir_start: int = (self.reserved_sectors_cnt + self.sectors_per_fat_cnt) * self.sector_size
return root_dir_start