fatfs_state.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Apache-2.0
  3. from textwrap import dedent
  4. from typing import Optional
  5. from .exceptions import InconsistentFATAttributes
  6. from .utils import (ALLOWED_SECTOR_SIZES, FAT12, FAT12_MAX_CLUSTERS, FAT16, FAT16_MAX_CLUSTERS,
  7. RESERVED_CLUSTERS_COUNT, FATDefaults, get_fat_sectors_count, get_fatfs_type,
  8. get_non_data_sectors_cnt, number_of_clusters)
  9. class FATFSState:
  10. """
  11. The class represents the state and the configuration of the FATFS.
  12. """
  13. def __init__(self,
  14. sector_size: int,
  15. reserved_sectors_cnt: int,
  16. root_dir_sectors_cnt: int,
  17. size: int,
  18. media_type: int,
  19. sectors_per_cluster: int,
  20. volume_label: str,
  21. oem_name: str,
  22. fat_tables_cnt: int,
  23. sec_per_track: int,
  24. num_heads: int,
  25. hidden_sectors: int,
  26. file_sys_type: str,
  27. use_default_datetime: bool,
  28. explicit_fat_type: Optional[int] = None,
  29. long_names_enabled: bool = False):
  30. self.boot_sector_state = BootSectorState(oem_name=oem_name,
  31. sector_size=sector_size,
  32. sectors_per_cluster=sectors_per_cluster,
  33. reserved_sectors_cnt=reserved_sectors_cnt,
  34. fat_tables_cnt=fat_tables_cnt,
  35. root_dir_sectors_cnt=root_dir_sectors_cnt,
  36. sectors_count=size // sector_size,
  37. media_type=media_type,
  38. sec_per_track=sec_per_track,
  39. num_heads=num_heads,
  40. hidden_sectors=hidden_sectors,
  41. volume_label=volume_label,
  42. file_sys_type=file_sys_type,
  43. volume_uuid=-1)
  44. self._explicit_fat_type: Optional[int] = explicit_fat_type
  45. self.long_names_enabled: bool = long_names_enabled
  46. self.use_default_datetime: bool = use_default_datetime
  47. if (size // sector_size) * sectors_per_cluster in (FAT12_MAX_CLUSTERS, FAT16_MAX_CLUSTERS):
  48. print('WARNING: It is not recommended to create FATFS with bounding '
  49. f'count of clusters: {FAT12_MAX_CLUSTERS} or {FAT16_MAX_CLUSTERS}')
  50. self.check_fat_type()
  51. @property
  52. def binary_image(self) -> bytearray:
  53. return self.boot_sector_state.binary_image
  54. @binary_image.setter
  55. def binary_image(self, value: bytearray) -> None:
  56. self.boot_sector_state.binary_image = value
  57. def check_fat_type(self) -> None:
  58. _type = self.boot_sector_state.fatfs_type
  59. if self._explicit_fat_type is not None and self._explicit_fat_type != _type:
  60. raise InconsistentFATAttributes(dedent(
  61. f"""FAT type you specified is inconsistent with other attributes of the system.
  62. The specified FATFS type: FAT{self._explicit_fat_type}
  63. The actual FATFS type: FAT{_type}"""))
  64. if _type not in (FAT12, FAT16):
  65. raise NotImplementedError('FAT32 is currently not supported.')
  66. class BootSectorState:
  67. # pylint: disable=too-many-instance-attributes
  68. def __init__(self,
  69. oem_name: str,
  70. sector_size: int,
  71. sectors_per_cluster: int,
  72. reserved_sectors_cnt: int,
  73. fat_tables_cnt: int,
  74. root_dir_sectors_cnt: int,
  75. sectors_count: int,
  76. media_type: int,
  77. sec_per_track: int,
  78. num_heads: int,
  79. hidden_sectors: int,
  80. volume_label: str,
  81. file_sys_type: str,
  82. volume_uuid: int = -1) -> None:
  83. self.oem_name: str = oem_name
  84. self.sector_size: int = sector_size
  85. assert self.sector_size in ALLOWED_SECTOR_SIZES
  86. self.sectors_per_cluster: int = sectors_per_cluster
  87. self.reserved_sectors_cnt: int = reserved_sectors_cnt
  88. self.fat_tables_cnt: int = fat_tables_cnt
  89. self.root_dir_sectors_cnt: int = root_dir_sectors_cnt
  90. self.sectors_count: int = sectors_count
  91. self.media_type: int = media_type
  92. self.sectors_per_fat_cnt = get_fat_sectors_count(self.size // self.sector_size, self.sector_size)
  93. self.sec_per_track: int = sec_per_track
  94. self.num_heads: int = num_heads
  95. self.hidden_sectors: int = hidden_sectors
  96. self.volume_label: str = volume_label
  97. self.file_sys_type: str = file_sys_type
  98. self.volume_uuid: int = volume_uuid
  99. self._binary_image: bytearray = bytearray(b'')
  100. @property
  101. def binary_image(self) -> bytearray:
  102. return self._binary_image
  103. @binary_image.setter
  104. def binary_image(self, value: bytearray) -> None:
  105. self._binary_image = value
  106. @property
  107. def size(self) -> int:
  108. return self.sector_size * self.sectors_count
  109. @property
  110. def data_region_start(self) -> int:
  111. return self.non_data_sectors * self.sector_size
  112. @property
  113. def fatfs_type(self) -> int:
  114. # variable typed_fatfs_type must be explicitly typed to avoid mypy error
  115. typed_fatfs_type: int = get_fatfs_type(self.clusters)
  116. return typed_fatfs_type
  117. @property
  118. def clusters(self) -> int:
  119. """
  120. The actual number of clusters is calculated by `number_of_clusters`,
  121. however, the initial two blocks of FAT are reserved (device type and root directory),
  122. despite they don't refer to the data region.
  123. Since that, two clusters are added to use the full potential of the FAT file system partition.
  124. """
  125. clusters_cnt_: int = number_of_clusters(self.data_sectors, self.sectors_per_cluster) + RESERVED_CLUSTERS_COUNT
  126. return clusters_cnt_
  127. @property
  128. def data_sectors(self) -> int:
  129. # self.sector_size is checked in constructor if has one of allowed values (ALLOWED_SECTOR_SIZES)
  130. return (self.size // self.sector_size) - self.non_data_sectors
  131. @property
  132. def non_data_sectors(self) -> int:
  133. non_data_sectors_: int = get_non_data_sectors_cnt(self.reserved_sectors_cnt,
  134. self.sectors_per_fat_cnt,
  135. self.root_dir_sectors_cnt)
  136. return non_data_sectors_
  137. @property
  138. def fat_table_start_address(self) -> int:
  139. return self.sector_size * self.reserved_sectors_cnt
  140. @property
  141. def entries_root_count(self) -> int:
  142. entries_root_count_: int = (self.root_dir_sectors_cnt * self.sector_size) // FATDefaults.ENTRY_SIZE
  143. return entries_root_count_
  144. @property
  145. def root_directory_start(self) -> int:
  146. root_dir_start: int = (self.reserved_sectors_cnt + self.sectors_per_fat_cnt) * self.sector_size
  147. return root_dir_start