Sat, 07 Nov 2015 13:23:07 +0100
Initial code from reprappro Marlin repository
0 | 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 "Marlin.h" | |
21 | #ifdef SDSUPPORT | |
22 | ||
23 | #include "SdVolume.h" | |
24 | //------------------------------------------------------------------------------ | |
25 | #if !USE_MULTIPLE_CARDS | |
26 | // raw block cache | |
27 | uint32_t SdVolume::cacheBlockNumber_; // current block number | |
28 | cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card | |
29 | Sd2Card* SdVolume::sdCard_; // pointer to SD card object | |
30 | bool SdVolume::cacheDirty_; // cacheFlush() will write block if true | |
31 | uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT | |
32 | #endif // USE_MULTIPLE_CARDS | |
33 | //------------------------------------------------------------------------------ | |
34 | // find a contiguous group of clusters | |
35 | bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { | |
36 | // start of group | |
37 | uint32_t bgnCluster; | |
38 | // end of group | |
39 | uint32_t endCluster; | |
40 | // last cluster of FAT | |
41 | uint32_t fatEnd = clusterCount_ + 1; | |
42 | ||
43 | // flag to save place to start next search | |
44 | bool setStart; | |
45 | ||
46 | // set search start cluster | |
47 | if (*curCluster) { | |
48 | // try to make file contiguous | |
49 | bgnCluster = *curCluster + 1; | |
50 | ||
51 | // don't save new start location | |
52 | setStart = false; | |
53 | } else { | |
54 | // start at likely place for free cluster | |
55 | bgnCluster = allocSearchStart_; | |
56 | ||
57 | // save next search start if one cluster | |
58 | setStart = count == 1; | |
59 | } | |
60 | // end of group | |
61 | endCluster = bgnCluster; | |
62 | ||
63 | // search the FAT for free clusters | |
64 | for (uint32_t n = 0;; n++, endCluster++) { | |
65 | // can't find space checked all clusters | |
66 | if (n >= clusterCount_) goto fail; | |
67 | ||
68 | // past end - start from beginning of FAT | |
69 | if (endCluster > fatEnd) { | |
70 | bgnCluster = endCluster = 2; | |
71 | } | |
72 | uint32_t f; | |
73 | if (!fatGet(endCluster, &f)) goto fail; | |
74 | ||
75 | if (f != 0) { | |
76 | // cluster in use try next cluster as bgnCluster | |
77 | bgnCluster = endCluster + 1; | |
78 | } else if ((endCluster - bgnCluster + 1) == count) { | |
79 | // done - found space | |
80 | break; | |
81 | } | |
82 | } | |
83 | // mark end of chain | |
84 | if (!fatPutEOC(endCluster)) goto fail; | |
85 | ||
86 | // link clusters | |
87 | while (endCluster > bgnCluster) { | |
88 | if (!fatPut(endCluster - 1, endCluster)) goto fail; | |
89 | endCluster--; | |
90 | } | |
91 | if (*curCluster != 0) { | |
92 | // connect chains | |
93 | if (!fatPut(*curCluster, bgnCluster)) goto fail; | |
94 | } | |
95 | // return first cluster number to caller | |
96 | *curCluster = bgnCluster; | |
97 | ||
98 | // remember possible next free cluster | |
99 | if (setStart) allocSearchStart_ = bgnCluster + 1; | |
100 | ||
101 | return true; | |
102 | ||
103 | fail: | |
104 | return false; | |
105 | } | |
106 | //------------------------------------------------------------------------------ | |
107 | bool SdVolume::cacheFlush() { | |
108 | if (cacheDirty_) { | |
109 | if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { | |
110 | goto fail; | |
111 | } | |
112 | // mirror FAT tables | |
113 | if (cacheMirrorBlock_) { | |
114 | if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { | |
115 | goto fail; | |
116 | } | |
117 | cacheMirrorBlock_ = 0; | |
118 | } | |
119 | cacheDirty_ = 0; | |
120 | } | |
121 | return true; | |
122 | ||
123 | fail: | |
124 | return false; | |
125 | } | |
126 | //------------------------------------------------------------------------------ | |
127 | bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) { | |
128 | if (cacheBlockNumber_ != blockNumber) { | |
129 | if (!cacheFlush()) goto fail; | |
130 | if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) goto fail; | |
131 | cacheBlockNumber_ = blockNumber; | |
132 | } | |
133 | if (dirty) cacheDirty_ = true; | |
134 | return true; | |
135 | ||
136 | fail: | |
137 | return false; | |
138 | } | |
139 | //------------------------------------------------------------------------------ | |
140 | // return the size in bytes of a cluster chain | |
141 | bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) { | |
142 | uint32_t s = 0; | |
143 | do { | |
144 | if (!fatGet(cluster, &cluster)) goto fail; | |
145 | s += 512UL << clusterSizeShift_; | |
146 | } while (!isEOC(cluster)); | |
147 | *size = s; | |
148 | return true; | |
149 | ||
150 | fail: | |
151 | return false; | |
152 | } | |
153 | //------------------------------------------------------------------------------ | |
154 | // Fetch a FAT entry | |
155 | bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { | |
156 | uint32_t lba; | |
157 | if (cluster > (clusterCount_ + 1)) goto fail; | |
158 | if (FAT12_SUPPORT && fatType_ == 12) { | |
159 | uint16_t index = cluster; | |
160 | index += index >> 1; | |
161 | lba = fatStartBlock_ + (index >> 9); | |
162 | if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail; | |
163 | index &= 0X1FF; | |
164 | uint16_t tmp = cacheBuffer_.data[index]; | |
165 | index++; | |
166 | if (index == 512) { | |
167 | if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) goto fail; | |
168 | index = 0; | |
169 | } | |
170 | tmp |= cacheBuffer_.data[index] << 8; | |
171 | *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; | |
172 | return true; | |
173 | } | |
174 | if (fatType_ == 16) { | |
175 | lba = fatStartBlock_ + (cluster >> 8); | |
176 | } else if (fatType_ == 32) { | |
177 | lba = fatStartBlock_ + (cluster >> 7); | |
178 | } else { | |
179 | goto fail; | |
180 | } | |
181 | if (lba != cacheBlockNumber_) { | |
182 | if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto fail; | |
183 | } | |
184 | if (fatType_ == 16) { | |
185 | *value = cacheBuffer_.fat16[cluster & 0XFF]; | |
186 | } else { | |
187 | *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK; | |
188 | } | |
189 | return true; | |
190 | ||
191 | fail: | |
192 | return false; | |
193 | } | |
194 | //------------------------------------------------------------------------------ | |
195 | // Store a FAT entry | |
196 | bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { | |
197 | uint32_t lba; | |
198 | // error if reserved cluster | |
199 | if (cluster < 2) goto fail; | |
200 | ||
201 | // error if not in FAT | |
202 | if (cluster > (clusterCount_ + 1)) goto fail; | |
203 | ||
204 | if (FAT12_SUPPORT && fatType_ == 12) { | |
205 | uint16_t index = cluster; | |
206 | index += index >> 1; | |
207 | lba = fatStartBlock_ + (index >> 9); | |
208 | if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; | |
209 | // mirror second FAT | |
210 | if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; | |
211 | index &= 0X1FF; | |
212 | uint8_t tmp = value; | |
213 | if (cluster & 1) { | |
214 | tmp = (cacheBuffer_.data[index] & 0XF) | tmp << 4; | |
215 | } | |
216 | cacheBuffer_.data[index] = tmp; | |
217 | index++; | |
218 | if (index == 512) { | |
219 | lba++; | |
220 | index = 0; | |
221 | if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; | |
222 | // mirror second FAT | |
223 | if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; | |
224 | } | |
225 | tmp = value >> 4; | |
226 | if (!(cluster & 1)) { | |
227 | tmp = ((cacheBuffer_.data[index] & 0XF0)) | tmp >> 4; | |
228 | } | |
229 | cacheBuffer_.data[index] = tmp; | |
230 | return true; | |
231 | } | |
232 | if (fatType_ == 16) { | |
233 | lba = fatStartBlock_ + (cluster >> 8); | |
234 | } else if (fatType_ == 32) { | |
235 | lba = fatStartBlock_ + (cluster >> 7); | |
236 | } else { | |
237 | goto fail; | |
238 | } | |
239 | if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto fail; | |
240 | // store entry | |
241 | if (fatType_ == 16) { | |
242 | cacheBuffer_.fat16[cluster & 0XFF] = value; | |
243 | } else { | |
244 | cacheBuffer_.fat32[cluster & 0X7F] = value; | |
245 | } | |
246 | // mirror second FAT | |
247 | if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; | |
248 | return true; | |
249 | ||
250 | fail: | |
251 | return false; | |
252 | } | |
253 | //------------------------------------------------------------------------------ | |
254 | // free a cluster chain | |
255 | bool SdVolume::freeChain(uint32_t cluster) { | |
256 | uint32_t next; | |
257 | ||
258 | // clear free cluster location | |
259 | allocSearchStart_ = 2; | |
260 | ||
261 | do { | |
262 | if (!fatGet(cluster, &next)) goto fail; | |
263 | ||
264 | // free cluster | |
265 | if (!fatPut(cluster, 0)) goto fail; | |
266 | ||
267 | cluster = next; | |
268 | } while (!isEOC(cluster)); | |
269 | ||
270 | return true; | |
271 | ||
272 | fail: | |
273 | return false; | |
274 | } | |
275 | //------------------------------------------------------------------------------ | |
276 | /** Volume free space in clusters. | |
277 | * | |
278 | * \return Count of free clusters for success or -1 if an error occurs. | |
279 | */ | |
280 | int32_t SdVolume::freeClusterCount() { | |
281 | uint32_t free = 0; | |
282 | uint16_t n; | |
283 | uint32_t todo = clusterCount_ + 2; | |
284 | ||
285 | if (fatType_ == 16) { | |
286 | n = 256; | |
287 | } else if (fatType_ == 32) { | |
288 | n = 128; | |
289 | } else { | |
290 | // put FAT12 here | |
291 | return -1; | |
292 | } | |
293 | ||
294 | for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) { | |
295 | if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1; | |
296 | if (todo < n) n = todo; | |
297 | if (fatType_ == 16) { | |
298 | for (uint16_t i = 0; i < n; i++) { | |
299 | if (cacheBuffer_.fat16[i] == 0) free++; | |
300 | } | |
301 | } else { | |
302 | for (uint16_t i = 0; i < n; i++) { | |
303 | if (cacheBuffer_.fat32[i] == 0) free++; | |
304 | } | |
305 | } | |
306 | } | |
307 | return free; | |
308 | } | |
309 | //------------------------------------------------------------------------------ | |
310 | /** Initialize a FAT volume. | |
311 | * | |
312 | * \param[in] dev The SD card where the volume is located. | |
313 | * | |
314 | * \param[in] part The partition to be used. Legal values for \a part are | |
315 | * 1-4 to use the corresponding partition on a device formatted with | |
316 | * a MBR, Master Boot Record, or zero if the device is formatted as | |
317 | * a super floppy with the FAT boot sector in block zero. | |
318 | * | |
319 | * \return The value one, true, is returned for success and | |
320 | * the value zero, false, is returned for failure. Reasons for | |
321 | * failure include not finding a valid partition, not finding a valid | |
322 | * FAT file system in the specified partition or an I/O error. | |
323 | */ | |
324 | bool SdVolume::init(Sd2Card* dev, uint8_t part) { | |
325 | uint32_t totalBlocks; | |
326 | uint32_t volumeStartBlock = 0; | |
327 | fat32_boot_t* fbs; | |
328 | ||
329 | sdCard_ = dev; | |
330 | fatType_ = 0; | |
331 | allocSearchStart_ = 2; | |
332 | cacheDirty_ = 0; // cacheFlush() will write block if true | |
333 | cacheMirrorBlock_ = 0; | |
334 | cacheBlockNumber_ = 0XFFFFFFFF; | |
335 | ||
336 | // if part == 0 assume super floppy with FAT boot sector in block zero | |
337 | // if part > 0 assume mbr volume with partition table | |
338 | if (part) { | |
339 | if (part > 4)goto fail; | |
340 | if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail; | |
341 | part_t* p = &cacheBuffer_.mbr.part[part-1]; | |
342 | if ((p->boot & 0X7F) !=0 || | |
343 | p->totalSectors < 100 || | |
344 | p->firstSector == 0) { | |
345 | // not a valid partition | |
346 | goto fail; | |
347 | } | |
348 | volumeStartBlock = p->firstSector; | |
349 | } | |
350 | if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto fail; | |
351 | fbs = &cacheBuffer_.fbs32; | |
352 | if (fbs->bytesPerSector != 512 || | |
353 | fbs->fatCount == 0 || | |
354 | fbs->reservedSectorCount == 0 || | |
355 | fbs->sectorsPerCluster == 0) { | |
356 | // not valid FAT volume | |
357 | goto fail; | |
358 | } | |
359 | fatCount_ = fbs->fatCount; | |
360 | blocksPerCluster_ = fbs->sectorsPerCluster; | |
361 | // determine shift that is same as multiply by blocksPerCluster_ | |
362 | clusterSizeShift_ = 0; | |
363 | while (blocksPerCluster_ != (1 << clusterSizeShift_)) { | |
364 | // error if not power of 2 | |
365 | if (clusterSizeShift_++ > 7) goto fail; | |
366 | } | |
367 | blocksPerFat_ = fbs->sectorsPerFat16 ? | |
368 | fbs->sectorsPerFat16 : fbs->sectorsPerFat32; | |
369 | ||
370 | fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount; | |
371 | ||
372 | // count for FAT16 zero for FAT32 | |
373 | rootDirEntryCount_ = fbs->rootDirEntryCount; | |
374 | ||
375 | // directory start for FAT16 dataStart for FAT32 | |
376 | rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_; | |
377 | ||
378 | // data start for FAT16 and FAT32 | |
379 | dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511)/512); | |
380 | ||
381 | // total blocks for FAT16 or FAT32 | |
382 | totalBlocks = fbs->totalSectors16 ? | |
383 | fbs->totalSectors16 : fbs->totalSectors32; | |
384 | // total data blocks | |
385 | clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); | |
386 | ||
387 | // divide by cluster size to get cluster count | |
388 | clusterCount_ >>= clusterSizeShift_; | |
389 | ||
390 | // FAT type is determined by cluster count | |
391 | if (clusterCount_ < 4085) { | |
392 | fatType_ = 12; | |
393 | if (!FAT12_SUPPORT) goto fail; | |
394 | } else if (clusterCount_ < 65525) { | |
395 | fatType_ = 16; | |
396 | } else { | |
397 | rootDirStart_ = fbs->fat32RootCluster; | |
398 | fatType_ = 32; | |
399 | } | |
400 | return true; | |
401 | ||
402 | fail: | |
403 | return false; | |
404 | } | |
405 | #endif |