# 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