aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/sd/SdVolume.h
blob: 2d57c681c4948de397db3a6cb58ccaf8e438ddaf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
 * Marlin 3D Printer Firmware
 * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
 *
 * Based on Sprinter and grbl.
 * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 *
 */
#pragma once

/**
 * sd/SdVolume.h
 *
 * Arduino SdFat Library
 * Copyright (c) 2009 by William Greiman
 *
 * This file is part of the Arduino Sd2Card Library
 */

#include <stdint.h>

#include "../inc/MarlinConfigPre.h"

#if ENABLED(USB_FLASH_DRIVE_SUPPORT)
  #include "usb_flashdrive/Sd2Card_FlashDrive.h"
#elif ENABLED(SDIO_SUPPORT)
  #include "Sd2Card_sdio.h"
#else
  #include "Sd2Card.h"
#endif

#include "SdFatConfig.h"
#include "SdFatStructs.h"

//==============================================================================
// SdVolume class
/**
 * \brief Cache for an SD data block
 */
union cache_t {
  uint8_t         data[512];  // Used to access cached file data blocks.
  uint16_t        fat16[256]; // Used to access cached FAT16 entries.
  uint32_t        fat32[128]; // Used to access cached FAT32 entries.
  dir_t           dir[16];    // Used to access cached directory entries.
  mbr_t           mbr;        // Used to access a cached Master Boot Record.
  fat_boot_t      fbs;        // Used to access to a cached FAT boot sector.
  fat32_boot_t    fbs32;      // Used to access to a cached FAT32 boot sector.
  fat32_fsinfo_t  fsinfo;     // Used to access to a cached FAT32 FSINFO sector.
};

/**
 * \class SdVolume
 * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
 */
class SdVolume {
 public:
  // Create an instance of SdVolume
  SdVolume() : fatType_(0) {}
  /**
   * Clear the cache and returns a pointer to the cache.  Used by the WaveRP
   * recorder to do raw write to the SD card.  Not for normal apps.
   * \return A pointer to the cache buffer or zero if an error occurs.
   */
  cache_t* cacheClear() {
    if (!cacheFlush()) return 0;
    cacheBlockNumber_ = 0xFFFFFFFF;
    return &cacheBuffer_;
  }

  /**
   * Initialize a FAT volume.  Try partition one first then try super
   * floppy format.
   *
   * \param[in] dev The Sd2Card where the volume is located.
   *
   * \return true for success, false for failure.
   * Reasons for failure include not finding a valid partition, not finding
   * a valid FAT file system or an I/O error.
   */
  bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0); }
  bool init(Sd2Card* dev, uint8_t part);

  // inline functions that return volume info
  uint8_t blocksPerCluster() const { return blocksPerCluster_; } //> \return The volume's cluster size in blocks.
  uint32_t blocksPerFat() const { return blocksPerFat_; }        //> \return The number of blocks in one FAT.
  uint32_t clusterCount() const { return clusterCount_; }        //> \return The total number of clusters in the volume.
  uint8_t clusterSizeShift() const { return clusterSizeShift_; } //> \return The shift count required to multiply by blocksPerCluster.
  uint32_t dataStartBlock() const { return dataStartBlock_; }    //> \return The logical block number for the start of file data.
  uint8_t fatCount() const { return fatCount_; }                 //> \return The number of FAT structures on the volume.
  uint32_t fatStartBlock() const { return fatStartBlock_; }      //> \return The logical block number for the start of the first FAT.
  uint8_t fatType() const { return fatType_; }                   //> \return The FAT type of the volume. Values are 12, 16 or 32.
  int32_t freeClusterCount();
  uint32_t rootDirEntryCount() const { return rootDirEntryCount_; } /** \return The number of entries in the root directory for FAT16 volumes. */

  /**
   * \return The logical block number for the start of the root directory
   *   on FAT16 volumes or the first cluster number on FAT32 volumes.
   */
  uint32_t rootDirStart() const { return rootDirStart_; }

  /**
   * Sd2Card object for this volume
   * \return pointer to Sd2Card object.
   */
  Sd2Card* sdCard() { return sdCard_; }

  /**
   * Debug access to FAT table
   *
   * \param[in] n cluster number.
   * \param[out] v value of entry
   * \return true for success or false for failure
   */
  bool dbgFat(uint32_t n, uint32_t* v) { return fatGet(n, v); }

 private:
  // Allow SdBaseFile access to SdVolume private data.
  friend class SdBaseFile;

  // value for dirty argument in cacheRawBlock to indicate read from cache
  static bool const CACHE_FOR_READ = false;
  // value for dirty argument in cacheRawBlock to indicate write to cache
  static bool const CACHE_FOR_WRITE = true;

  #if USE_MULTIPLE_CARDS
    cache_t cacheBuffer_;        // 512 byte cache for device blocks
    uint32_t cacheBlockNumber_;  // Logical number of block in the cache
    Sd2Card* sdCard_;            // Sd2Card object for cache
    bool cacheDirty_;            // cacheFlush() will write block if true
    uint32_t cacheMirrorBlock_;  // block number for mirror FAT
  #else
    static cache_t cacheBuffer_;        // 512 byte cache for device blocks
    static uint32_t cacheBlockNumber_;  // Logical number of block in the cache
    static Sd2Card* sdCard_;            // Sd2Card object for cache
    static bool cacheDirty_;            // cacheFlush() will write block if true
    static uint32_t cacheMirrorBlock_;  // block number for mirror FAT
  #endif

  uint32_t allocSearchStart_;   // start cluster for alloc search
  uint8_t blocksPerCluster_;    // cluster size in blocks
  uint32_t blocksPerFat_;       // FAT size in blocks
  uint32_t clusterCount_;       // clusters in one FAT
  uint8_t clusterSizeShift_;    // shift to convert cluster count to block count
  uint32_t dataStartBlock_;     // first data block number
  uint8_t fatCount_;            // number of FATs on volume
  uint32_t fatStartBlock_;      // start block for first FAT
  uint8_t fatType_;             // volume type (12, 16, OR 32)
  uint16_t rootDirEntryCount_;  // number of entries in FAT16 root dir
  uint32_t rootDirStart_;       // root start block for FAT16, cluster for FAT32

  bool allocContiguous(uint32_t count, uint32_t* curCluster);
  uint8_t blockOfCluster(uint32_t position) const { return (position >> 9) & (blocksPerCluster_ - 1); }
  uint32_t clusterStartBlock(uint32_t cluster) const { return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); }
  uint32_t blockNumber(uint32_t cluster, uint32_t position) const { return clusterStartBlock(cluster) + blockOfCluster(position); }

  cache_t* cache() { return &cacheBuffer_; }
  uint32_t cacheBlockNumber() const { return cacheBlockNumber_; }

  #if USE_MULTIPLE_CARDS
    bool cacheFlush();
    bool cacheRawBlock(uint32_t blockNumber, bool dirty);
  #else
    static bool cacheFlush();
    static bool cacheRawBlock(uint32_t blockNumber, bool dirty);
  #endif

  // used by SdBaseFile write to assign cache to SD location
  void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) {
    cacheDirty_ = dirty;
    cacheBlockNumber_  = blockNumber;
  }
  void cacheSetDirty() { cacheDirty_ |= CACHE_FOR_WRITE; }
  bool chainSize(uint32_t beginCluster, uint32_t* size);
  bool fatGet(uint32_t cluster, uint32_t* value);
  bool fatPut(uint32_t cluster, uint32_t value);
  bool fatPutEOC(uint32_t cluster) { return fatPut(cluster, 0x0FFFFFFF); }
  bool freeChain(uint32_t cluster);
  bool isEOC(uint32_t cluster) const {
    if (FAT12_SUPPORT && fatType_ == 12) return  cluster >= FAT12EOC_MIN;
    if (fatType_ == 16) return cluster >= FAT16EOC_MIN;
    return  cluster >= FAT32EOC_MIN;
  }
  bool readBlock(uint32_t block, uint8_t* dst) { return sdCard_->readBlock(block, dst); }
  bool writeBlock(uint32_t block, const uint8_t* dst) { return sdCard_->writeBlock(block, dst); }
};