]> git.piffa.net Git - arduino/blob - books/pdummies/Libraries/SD/utility/SdVolume.cpp
first commit
[arduino] / books / pdummies / Libraries / SD / utility / SdVolume.cpp
1 /* Arduino SdFat Library
2  * Copyright (C) 2009 by William Greiman
3  *
4  * This file is part of the Arduino SdFat Library
5  *
6  * This Library is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with the Arduino SdFat Library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 #include <SdFat.h>
21 //------------------------------------------------------------------------------
22 // raw block cache
23 // init cacheBlockNumber_to invalid SD block number
24 uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
25 cache_t  SdVolume::cacheBuffer_;     // 512 byte cache for Sd2Card
26 Sd2Card* SdVolume::sdCard_;          // pointer to SD card object
27 uint8_t  SdVolume::cacheDirty_ = 0;  // cacheFlush() will write block if true
28 uint32_t SdVolume::cacheMirrorBlock_ = 0;  // mirror  block for second FAT
29 //------------------------------------------------------------------------------
30 // find a contiguous group of clusters
31 uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
32   // start of group
33   uint32_t bgnCluster;
34
35   // flag to save place to start next search
36   uint8_t setStart;
37
38   // set search start cluster
39   if (*curCluster) {
40     // try to make file contiguous
41     bgnCluster = *curCluster + 1;
42
43     // don't save new start location
44     setStart = false;
45   } else {
46     // start at likely place for free cluster
47     bgnCluster = allocSearchStart_;
48
49     // save next search start if one cluster
50     setStart = 1 == count;
51   }
52   // end of group
53   uint32_t endCluster = bgnCluster;
54
55   // last cluster of FAT
56   uint32_t fatEnd = clusterCount_ + 1;
57
58   // search the FAT for free clusters
59   for (uint32_t n = 0;; n++, endCluster++) {
60     // can't find space checked all clusters
61     if (n >= clusterCount_) return false;
62
63     // past end - start from beginning of FAT
64     if (endCluster > fatEnd) {
65       bgnCluster = endCluster = 2;
66     }
67     uint32_t f;
68     if (!fatGet(endCluster, &f)) return false;
69
70     if (f != 0) {
71       // cluster in use try next cluster as bgnCluster
72       bgnCluster = endCluster + 1;
73     } else if ((endCluster - bgnCluster + 1) == count) {
74       // done - found space
75       break;
76     }
77   }
78   // mark end of chain
79   if (!fatPutEOC(endCluster)) return false;
80
81   // link clusters
82   while (endCluster > bgnCluster) {
83     if (!fatPut(endCluster - 1, endCluster)) return false;
84     endCluster--;
85   }
86   if (*curCluster != 0) {
87     // connect chains
88     if (!fatPut(*curCluster, bgnCluster)) return false;
89   }
90   // return first cluster number to caller
91   *curCluster = bgnCluster;
92
93   // remember possible next free cluster
94   if (setStart) allocSearchStart_ = bgnCluster + 1;
95
96   return true;
97 }
98 //------------------------------------------------------------------------------
99 uint8_t SdVolume::cacheFlush(void) {
100   if (cacheDirty_) {
101     if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) {
102       return false;
103     }
104     // mirror FAT tables
105     if (cacheMirrorBlock_) {
106       if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) {
107         return false;
108       }
109       cacheMirrorBlock_ = 0;
110     }
111     cacheDirty_ = 0;
112   }
113   return true;
114 }
115 //------------------------------------------------------------------------------
116 uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) {
117   if (cacheBlockNumber_ != blockNumber) {
118     if (!cacheFlush()) return false;
119     if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false;
120     cacheBlockNumber_ = blockNumber;
121   }
122   cacheDirty_ |= action;
123   return true;
124 }
125 //------------------------------------------------------------------------------
126 // cache a zero block for blockNumber
127 uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) {
128   if (!cacheFlush()) return false;
129
130   // loop take less flash than memset(cacheBuffer_.data, 0, 512);
131   for (uint16_t i = 0; i < 512; i++) {
132     cacheBuffer_.data[i] = 0;
133   }
134   cacheBlockNumber_ = blockNumber;
135   cacheSetDirty();
136   return true;
137 }
138 //------------------------------------------------------------------------------
139 // return the size in bytes of a cluster chain
140 uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const {
141   uint32_t s = 0;
142   do {
143     if (!fatGet(cluster, &cluster)) return false;
144     s += 512UL << clusterSizeShift_;
145   } while (!isEOC(cluster));
146   *size = s;
147   return true;
148 }
149 //------------------------------------------------------------------------------
150 // Fetch a FAT entry
151 uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const {
152   if (cluster > (clusterCount_ + 1)) return false;
153   uint32_t lba = fatStartBlock_;
154   lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
155   if (lba != cacheBlockNumber_) {
156     if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
157   }
158   if (fatType_ == 16) {
159     *value = cacheBuffer_.fat16[cluster & 0XFF];
160   } else {
161     *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK;
162   }
163   return true;
164 }
165 //------------------------------------------------------------------------------
166 // Store a FAT entry
167 uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) {
168   // error if reserved cluster
169   if (cluster < 2) return false;
170
171   // error if not in FAT
172   if (cluster > (clusterCount_ + 1)) return false;
173
174   // calculate block address for entry
175   uint32_t lba = fatStartBlock_;
176   lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
177
178   if (lba != cacheBlockNumber_) {
179     if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
180   }
181   // store entry
182   if (fatType_ == 16) {
183     cacheBuffer_.fat16[cluster & 0XFF] = value;
184   } else {
185     cacheBuffer_.fat32[cluster & 0X7F] = value;
186   }
187   cacheSetDirty();
188
189   // mirror second FAT
190   if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
191   return true;
192 }
193 //------------------------------------------------------------------------------
194 // free a cluster chain
195 uint8_t SdVolume::freeChain(uint32_t cluster) {
196   // clear free cluster location
197   allocSearchStart_ = 2;
198
199   do {
200     uint32_t next;
201     if (!fatGet(cluster, &next)) return false;
202
203     // free cluster
204     if (!fatPut(cluster, 0)) return false;
205
206     cluster = next;
207   } while (!isEOC(cluster));
208
209   return true;
210 }
211 //------------------------------------------------------------------------------
212 /**
213  * Initialize a FAT volume.
214  *
215  * \param[in] dev The SD card where the volume is located.
216  *
217  * \param[in] part The partition to be used.  Legal values for \a part are
218  * 1-4 to use the corresponding partition on a device formatted with
219  * a MBR, Master Boot Record, or zero if the device is formatted as
220  * a super floppy with the FAT boot sector in block zero.
221  *
222  * \return The value one, true, is returned for success and
223  * the value zero, false, is returned for failure.  Reasons for
224  * failure include not finding a valid partition, not finding a valid
225  * FAT file system in the specified partition or an I/O error.
226  */
227 uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) {
228   uint32_t volumeStartBlock = 0;
229   sdCard_ = dev;
230   // if part == 0 assume super floppy with FAT boot sector in block zero
231   // if part > 0 assume mbr volume with partition table
232   if (part) {
233     if (part > 4)return false;
234     if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
235     part_t* p = &cacheBuffer_.mbr.part[part-1];
236     if ((p->boot & 0X7F) !=0  ||
237       p->totalSectors < 100 ||
238       p->firstSector == 0) {
239       // not a valid partition
240       return false;
241     }
242     volumeStartBlock = p->firstSector;
243   }
244   if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
245   bpb_t* bpb = &cacheBuffer_.fbs.bpb;
246   if (bpb->bytesPerSector != 512 ||
247     bpb->fatCount == 0 ||
248     bpb->reservedSectorCount == 0 ||
249     bpb->sectorsPerCluster == 0) {
250        // not valid FAT volume
251       return false;
252   }
253   fatCount_ = bpb->fatCount;
254   blocksPerCluster_ = bpb->sectorsPerCluster;
255
256   // determine shift that is same as multiply by blocksPerCluster_
257   clusterSizeShift_ = 0;
258   while (blocksPerCluster_ != (1 << clusterSizeShift_)) {
259     // error if not power of 2
260     if (clusterSizeShift_++ > 7) return false;
261   }
262   blocksPerFat_ = bpb->sectorsPerFat16 ?
263                     bpb->sectorsPerFat16 : bpb->sectorsPerFat32;
264
265   fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount;
266
267   // count for FAT16 zero for FAT32
268   rootDirEntryCount_ = bpb->rootDirEntryCount;
269
270   // directory start for FAT16 dataStart for FAT32
271   rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_;
272
273   // data start for FAT16 and FAT32
274   dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511)/512);
275
276   // total blocks for FAT16 or FAT32
277   uint32_t totalBlocks = bpb->totalSectors16 ?
278                            bpb->totalSectors16 : bpb->totalSectors32;
279   // total data blocks
280   clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
281
282   // divide by cluster size to get cluster count
283   clusterCount_ >>= clusterSizeShift_;
284
285   // FAT type is determined by cluster count
286   if (clusterCount_ < 4085) {
287     fatType_ = 12;
288   } else if (clusterCount_ < 65525) {
289     fatType_ = 16;
290   } else {
291     rootDirStart_ = bpb->fat32RootCluster;
292     fatType_ = 32;
293   }
294   return true;
295 }