- Import FullFAT and add it to build.
[reactos.git] / reactos / lib / 3rdparty / fullfat / ff_ioman.c
1 /*****************************************************************************
2 * FullFAT - High Performance, Thread-Safe Embedded FAT File-System *
3 * Copyright (C) 2009 James Walmsley (james@worm.me.uk) *
4 * *
5 * This program is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 * *
18 * IMPORTANT NOTICE: *
19 * ================= *
20 * Alternative Licensing is available directly from the Copyright holder, *
21 * (James Walmsley). For more information consult LICENSING.TXT to obtain *
22 * a Commercial license. *
23 * *
24 * See RESTRICTIONS.TXT for extra restrictions on the use of FullFAT. *
25 * *
26 * Removing the above notice is illegal and will invalidate this license. *
27 *****************************************************************************
28 * See http://worm.me.uk/fullfat for more information. *
29 * Or http://fullfat.googlecode.com/ for latest releases and the wiki. *
30 *****************************************************************************/
31
32 /**
33 * @file ff_ioman.c
34 * @author James Walmsley
35 * @ingroup IOMAN
36 *
37 * @defgroup IOMAN I/O Manager
38 * @brief Handles IO buffers for FullFAT safely.
39 *
40 * Provides a simple static interface to the rest of FullFAT to manage
41 * buffers. It also defines the public interfaces for Creating and
42 * Destroying a FullFAT IO object.
43 **/
44
45 #include "ff_ioman.h" // Includes ff_types.h, ff_safety.h, <stdio.h>
46 #include "fat.h"
47
48 extern FF_T_UINT32 FF_FindFreeCluster (FF_IOMAN *pIoman);
49 extern FF_T_UINT32 FF_CountFreeClusters (FF_IOMAN *pIoman);
50
51 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN *pIoman);
52
53 /**
54 * @public
55 * @brief Creates an FF_IOMAN object, to initialise FullFAT
56 *
57 * @param pCacheMem Pointer to a buffer for the cache. (NULL if ok to Malloc).
58 * @param Size The size of the provided buffer, or size of the cache to be created.
59 * @param BlkSize The block size of devices to be attached. If in doubt use 512.
60 * @param pError Pointer to a signed byte for error checking. Can be NULL if not required.
61 * @param pError To be checked when a NULL pointer is returned.
62 *
63 * @return Returns a pointer to an FF_IOMAN type object. NULL on Error, check the contents of
64 * @return pError
65 **/
66 FF_IOMAN *FF_CreateIOMAN(FF_T_UINT8 *pCacheMem, FF_T_UINT32 Size, FF_T_UINT16 BlkSize, FF_ERROR *pError) {
67
68 FF_IOMAN *pIoman = NULL;
69 FF_T_UINT32 *pLong = NULL; // Force malloc to malloc memory on a 32-bit boundary.
70 #ifdef FF_PATH_CACHE
71 FF_T_UINT32 i;
72 #endif
73
74 if(pError) {
75 *pError = FF_ERR_NONE;
76 }
77
78 if((BlkSize % 512) != 0 || Size == 0) {
79 if(pError) {
80 *pError = FF_ERR_IOMAN_BAD_BLKSIZE;
81 }
82 return NULL; // BlkSize Size not a multiple of 512 > 0
83 }
84
85 if((Size % BlkSize) != 0 || Size == 0) {
86 if(pError) {
87 *pError = FF_ERR_IOMAN_BAD_MEMSIZE;
88 }
89 return NULL; // Memory Size not a multiple of BlkSize > 0
90 }
91
92 pIoman = (FF_IOMAN *) malloc(sizeof(FF_IOMAN));
93
94 if(!pIoman) { // Ensure malloc() succeeded.
95 if(pError) {
96 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
97 }
98 return NULL;
99 }
100
101 // This is just a bit-mask, to use a byte to keep track of memory.
102 // pIoman->MemAllocation = 0x00; // Unset all allocation identifiers.
103 pIoman->pBlkDevice = NULL;
104 pIoman->pBuffers = NULL;
105 pIoman->pCacheMem = NULL;
106 pIoman->pPartition = NULL;
107 pIoman->pSemaphore = NULL;
108
109 pIoman->pPartition = (FF_PARTITION *) malloc(sizeof(FF_PARTITION));
110 if(pIoman->pPartition) { // If succeeded, flag that allocation.
111 pIoman->MemAllocation |= FF_IOMAN_ALLOC_PART;
112 pIoman->pPartition->LastFreeCluster = 0;
113 pIoman->pPartition->PartitionMounted = FF_FALSE; // This should be checked by FF_Open();
114 #ifdef FF_PATH_CACHE
115 pIoman->pPartition->PCIndex = 0;
116 for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) {
117 pIoman->pPartition->PathCache[i].DirCluster = 0;
118 pIoman->pPartition->PathCache[i].Path[0] = '\0';
119 #ifdef FF_HASH_TABLE_SUPPORT
120 pIoman->pPartition->PathCache[i].pHashTable = FF_CreateHashTable();
121 pIoman->pPartition->PathCache[i].bHashed = FF_FALSE;
122 #endif
123 }
124 #endif
125 } else {
126 FF_DestroyIOMAN(pIoman);
127 return NULL;
128 }
129
130 pIoman->pBlkDevice = (FF_BLK_DEVICE *) malloc(sizeof(FF_BLK_DEVICE));
131 if(pIoman->pBlkDevice) { // If succeeded, flag that allocation.
132 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BLKDEV;
133
134 // Make sure all pointers are NULL
135 pIoman->pBlkDevice->fnReadBlocks = NULL;
136 pIoman->pBlkDevice->fnWriteBlocks = NULL;
137 pIoman->pBlkDevice->pParam = NULL;
138
139 } else {
140 FF_DestroyIOMAN(pIoman);
141 return NULL;
142 }
143
144 // Organise the memory provided, or create our own!
145 if(pCacheMem) {
146 pIoman->pCacheMem = pCacheMem;
147 }else { // No-Cache buffer provided (malloc)
148 pLong = (FF_T_UINT32 *) malloc(Size);
149 pIoman->pCacheMem = (FF_T_UINT8 *) pLong;
150 if(!pIoman->pCacheMem) {
151 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFFERS;
152 FF_DestroyIOMAN(pIoman);
153 return NULL;
154 }
155
156 }
157
158 pIoman->BlkSize = BlkSize;
159 pIoman->CacheSize = (FF_T_UINT16) (Size / BlkSize);
160 pIoman->FirstFile = NULL;
161 pIoman->Locks = 0;
162
163 /* Malloc() memory for buffer objects. (FullFAT never refers to a buffer directly
164 but uses buffer objects instead. Allows us to provide thread safety.
165 */
166 pIoman->pBuffers = (FF_BUFFER *) malloc(sizeof(FF_BUFFER) * pIoman->CacheSize);
167
168 if(pIoman->pBuffers) {
169 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFDESCR;
170 FF_IOMAN_InitBufferDescriptors(pIoman);
171 } else {
172 FF_DestroyIOMAN(pIoman);
173 }
174
175 // Finally create a Semaphore for Buffer Description modifications.
176 pIoman->pSemaphore = FF_CreateSemaphore();
177
178 return pIoman; // Sucess, return the created object.
179 }
180
181 /**
182 * @public
183 * @brief Destroys an FF_IOMAN object, and frees all assigned memory.
184 *
185 * @param pIoman Pointer to an FF_IOMAN object, as returned from FF_CreateIOMAN.
186 *
187 * @return FF_ERR_NONE on sucess, or a documented error code on failure. (FF_ERR_NULL_POINTER)
188 *
189 **/
190 FF_ERROR FF_DestroyIOMAN(FF_IOMAN *pIoman) {
191
192 // Ensure no NULL pointer was provided.
193 if(!pIoman) {
194 return FF_ERR_NULL_POINTER;
195 }
196
197 // Ensure pPartition pointer was allocated.
198 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_PART)) {
199 free(pIoman->pPartition);
200 }
201
202 // Ensure pBlkDevice pointer was allocated.
203 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BLKDEV)) {
204 free(pIoman->pBlkDevice);
205 }
206
207 // Ensure pBuffers pointer was allocated.
208 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BUFDESCR)) {
209 free(pIoman->pBuffers);
210 }
211
212 // Ensure pCacheMem pointer was allocated.
213 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BUFFERS)) {
214 free(pIoman->pCacheMem);
215 }
216
217 // Destroy any Semaphore that was created.
218 if(pIoman->pSemaphore) {
219 FF_DestroySemaphore(pIoman->pSemaphore);
220 }
221
222 // Finally free the FF_IOMAN object.
223 free(pIoman);
224
225 return FF_ERR_NONE;
226 }
227
228 /**
229 * @private
230 * @brief Initialises Buffer Descriptions as part of the FF_IOMAN object initialisation.
231 *
232 * @param pIoman IOMAN Object.
233 *
234 **/
235 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN *pIoman) {
236 FF_T_UINT16 i;
237 FF_BUFFER *pBuffer = pIoman->pBuffers;
238 pIoman->LastReplaced = 0;
239 for(i = 0; i < pIoman->CacheSize; i++) {
240 pBuffer->Mode = 0;
241 pBuffer->NumHandles = 0;
242 pBuffer->Persistance = 0;
243 pBuffer->LRU = 0;
244 pBuffer->Sector = 0;
245 pBuffer->pBuffer = (FF_T_UINT8 *)((pIoman->pCacheMem) + (pIoman->BlkSize * i));
246 pBuffer->Modified = FF_FALSE;
247 pBuffer->Valid = FF_FALSE;
248 pBuffer++;
249 }
250 }
251
252 /**
253 * @private
254 * @brief Tests the Mode for validity.
255 *
256 * @param Mode Mode of buffer to check.
257 *
258 * @return FF_TRUE when valid, else FF_FALSE.
259 **/
260 /*static FF_T_BOOL FF_IOMAN_ModeValid(FF_T_UINT8 Mode) {
261 if(Mode == FF_MODE_READ || Mode == FF_MODE_WRITE) {
262 return FF_TRUE;
263 }
264 return FF_FALSE;
265 }*/
266
267
268 /**
269 * @private
270 * @brief Fills a buffer with the appropriate sector via the device driver.
271 *
272 * @param pIoman FF_IOMAN object.
273 * @param Sector LBA address of the sector to fetch.
274 * @param pBuffer Pointer to a byte-wise buffer to store the fetched data.
275 *
276 * @return FF_TRUE when valid, else FF_FALSE.
277 **/
278 static FF_ERROR FF_IOMAN_FillBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 *pBuffer) {
279 FF_T_SINT32 retVal = 0;
280 if(pIoman->pBlkDevice->fnReadBlocks) { // Make sure we don't execute a NULL.
281 do{
282 retVal = pIoman->pBlkDevice->fnReadBlocks(pBuffer, Sector, 1, pIoman->pBlkDevice->pParam);
283 if(retVal == FF_ERR_DRIVER_BUSY) {
284 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
285 }
286 } while(retVal == FF_ERR_DRIVER_BUSY);
287 if(retVal < 0) {
288 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
289 } else {
290 if(retVal == 1) {
291 return 0; // 1 Block was sucessfully read.
292 } else {
293 return -1; // 0 Blocks we're read, Error!
294 }
295 }
296 }
297 return -1; // error no device diver registered.
298 }
299
300
301 /**
302 * @private
303 * @brief Flushes a buffer to the device driver.
304 *
305 * @param pIoman FF_IOMAN object.
306 * @param Sector LBA address of the sector to fetch.
307 * @param pBuffer Pointer to a byte-wise buffer to store the fetched data.
308 *
309 * @return FF_TRUE when valid, else FF_FALSE.
310 **/
311 static FF_ERROR FF_IOMAN_FlushBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 *pBuffer) {
312 FF_T_SINT32 retVal = 0;
313 if(pIoman->pBlkDevice->fnWriteBlocks) { // Make sure we don't execute a NULL.
314 do{
315 retVal = pIoman->pBlkDevice->fnWriteBlocks(pBuffer, Sector, 1, pIoman->pBlkDevice->pParam);
316 if(retVal == FF_ERR_DRIVER_BUSY) {
317 FF_Sleep(FF_DRIVER_BUSY_SLEEP);
318 }
319 } while(retVal == FF_ERR_DRIVER_BUSY);
320 if(retVal < 0) {
321 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
322 } else {
323 if(retVal == 1) {
324 return FF_ERR_NONE; // 1 Block was sucessfully written.
325 } else {
326 return -1; // 0 Blocks we're written, Error!
327 }
328 }
329 }
330 return -1; // error no device diver registered.
331 }
332
333
334 /**
335 * @private
336 * @brief Flushes all Write cache buffers with no active Handles.
337 *
338 * @param pIoman IOMAN Object.
339 *
340 * @return FF_ERR_NONE on Success.
341 **/
342 FF_ERROR FF_FlushCache(FF_IOMAN *pIoman) {
343
344 FF_T_UINT16 i,x;
345
346 if(!pIoman) {
347 return FF_ERR_NULL_POINTER;
348 }
349
350 FF_PendSemaphore(pIoman->pSemaphore);
351 {
352 for(i = 0; i < pIoman->CacheSize; i++) {
353 if((pIoman->pBuffers + i)->NumHandles == 0 && (pIoman->pBuffers + i)->Modified == FF_TRUE) {
354
355 FF_IOMAN_FlushBuffer(pIoman, (pIoman->pBuffers + i)->Sector, (pIoman->pBuffers + i)->pBuffer);
356
357 // Buffer has now been flushed, mark it as a read buffer and unmodified.
358 (pIoman->pBuffers + i)->Mode = FF_MODE_READ;
359 (pIoman->pBuffers + i)->Modified = FF_FALSE;
360
361 // Search for other buffers that used this sector, and mark them as modified
362 // So that further requests will result in the new sector being fetched.
363 for(x = 0; x < pIoman->CacheSize; x++) {
364 if(x != i) {
365 if((pIoman->pBuffers + x)->Sector == (pIoman->pBuffers + i)->Sector && (pIoman->pBuffers + x)->Mode == FF_MODE_READ) {
366 (pIoman->pBuffers + x)->Modified = FF_TRUE;
367 }
368 }
369 }
370 }
371 }
372 }
373 FF_ReleaseSemaphore(pIoman->pSemaphore);
374
375 return FF_ERR_NONE;
376 }
377
378 /*static FF_T_BOOL FF_isFATSector(FF_IOMAN *pIoman, FF_T_UINT32 Sector) {
379 if(Sector >= pIoman->pPartition->FatBeginLBA && Sector < (pIoman->pPartition->FatBeginLBA + pIoman->pPartition->ReservedSectors)) {
380 return FF_TRUE;
381 }
382 return FF_FALSE;
383 }*/
384
385 FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) {
386 FF_BUFFER *pBuffer;
387 FF_BUFFER *pBufLRU = NULL;
388 FF_BUFFER *pBufLHITS = NULL;
389 FF_BUFFER *pBufMatch = NULL;
390
391 FF_T_UINT32 i;
392
393 while(!pBufMatch) {
394 FF_PendSemaphore(pIoman->pSemaphore);
395 {
396 for(i = 0; i < pIoman->CacheSize; i++) {
397 pBuffer = (pIoman->pBuffers + i);
398 if(pBuffer->Sector == Sector && pBuffer->Valid == FF_TRUE) {
399 pBufMatch = pBuffer;
400 } else {
401 if(pBuffer->NumHandles == 0) {
402 pBuffer->LRU += 1;
403
404 if(!pBufLRU) {
405 pBufLRU = pBuffer;
406 }
407 if(!pBufLHITS) {
408 pBufLHITS = pBuffer;
409 }
410
411 if(pBuffer->LRU >= pBufLRU->LRU) {
412 if(pBuffer->LRU == pBufLRU->LRU) {
413 if(pBuffer->Persistance > pBufLRU->Persistance) {
414 pBufLRU = pBuffer;
415 }
416 } else {
417 pBufLRU = pBuffer;
418 }
419 }
420
421 if(pBuffer->Persistance < pBufLHITS->Persistance) {
422 pBufLHITS = pBuffer;
423 }
424 }
425 }
426 }
427
428 if(pBufMatch) {
429 // A Match was found process!
430 if(Mode == FF_MODE_READ && pBufMatch->Mode == FF_MODE_READ) {
431 pBufMatch->NumHandles += 1;
432 pBufMatch->Persistance += 1;
433 FF_ReleaseSemaphore(pIoman->pSemaphore);
434 return pBufMatch;
435 }
436
437 if(pBufMatch->Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) { // This buffer has no attached handles.
438 pBufMatch->Mode = Mode;
439 pBufMatch->NumHandles = 1;
440 pBufMatch->Persistance += 1;
441 FF_ReleaseSemaphore(pIoman->pSemaphore);
442 return pBufMatch;
443 }
444
445 if(pBufMatch->Mode == FF_MODE_READ && Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) {
446 pBufMatch->Mode = Mode;
447 pBufMatch->Modified = FF_TRUE;
448 pBufMatch->NumHandles = 1;
449 pBufMatch->Persistance += 1;
450 FF_ReleaseSemaphore(pIoman->pSemaphore);
451 return pBufMatch;
452 }
453
454 pBufMatch = NULL; // Sector is already in use, keep yielding until its available!
455
456 } else {
457 // Choose a suitable buffer!
458 if(pBufLRU) {
459 // Process the suitable candidate.
460 if(pBufLRU->Modified == FF_TRUE) {
461 FF_IOMAN_FlushBuffer(pIoman, pBufLRU->Sector, pBufLRU->pBuffer);
462 }
463 pBufLRU->Mode = Mode;
464 pBufLRU->Persistance = 1;
465 pBufLRU->LRU = 0;
466 pBufLRU->NumHandles = 1;
467 pBufLRU->Sector = Sector;
468
469 if(Mode == FF_MODE_WRITE) {
470 pBufLRU->Modified = FF_TRUE;
471 } else {
472 pBufLRU->Modified = FF_FALSE;
473 }
474
475 FF_IOMAN_FillBuffer(pIoman, Sector, pBufLRU->pBuffer);
476 pBufLRU->Valid = FF_TRUE;
477 FF_ReleaseSemaphore(pIoman->pSemaphore);
478 return pBufLRU;
479 }
480
481 }
482 }
483 FF_ReleaseSemaphore(pIoman->pSemaphore);
484 FF_Yield();
485 }
486
487 return pBufMatch; // Return the Matched Buffer!
488 }
489
490
491 /**
492 * @private
493 * @brief Releases a buffer resource.
494 *
495 * @param pIoman Pointer to an FF_IOMAN object.
496 * @param pBuffer Pointer to an FF_BUFFER object.
497 *
498 **/
499 void FF_ReleaseBuffer(FF_IOMAN *pIoman, FF_BUFFER *pBuffer) {
500 // Protect description changes with a semaphore.
501 FF_PendSemaphore(pIoman->pSemaphore);
502 {
503 pBuffer->NumHandles--;
504 }
505 FF_ReleaseSemaphore(pIoman->pSemaphore);
506 }
507
508 /**
509 * @public
510 * @brief Registers a device driver with FullFAT
511 *
512 * The device drivers must adhere to the specification provided by
513 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
514 *
515 * @param pIoman FF_IOMAN object.
516 * @param BlkSize Block Size that the driver deals in. (Minimum 512, larger values must be a multiple of 512).
517 * @param fnWriteBlocks Pointer to the Write Blocks to device function, as described by FF_WRITE_BLOCKS.
518 * @param fnReadBlocks Pointer to the Read Blocks from device function, as described by FF_READ_BLOCKS.
519 * @param pParam Pointer to a parameter for use in the functions.
520 *
521 * @return 0 on success, FF_ERR_IOMAN_DEV_ALREADY_REGD if a device was already hooked, FF_ERR_IOMAN_NULL_POINTER if a pIoman object wasn't provided.
522 **/
523 FF_ERROR FF_RegisterBlkDevice(FF_IOMAN *pIoman, FF_T_UINT16 BlkSize, FF_WRITE_BLOCKS fnWriteBlocks, FF_READ_BLOCKS fnReadBlocks, void *pParam) {
524 if(!pIoman) { // We can't do anything without an IOMAN object.
525 return FF_ERR_NULL_POINTER;
526 }
527
528 if((BlkSize % 512) != 0 || BlkSize == 0) {
529 return FF_ERR_IOMAN_DEV_INVALID_BLKSIZE; // BlkSize Size not a multiple of IOMAN's Expected BlockSize > 0
530 }
531
532 if((BlkSize % pIoman->BlkSize) != 0 || BlkSize == 0) {
533 return FF_ERR_IOMAN_DEV_INVALID_BLKSIZE; // BlkSize Size not a multiple of IOMAN's Expected BlockSize > 0
534 }
535
536 // Ensure that a device cannot be re-registered "mid-flight"
537 // Doing so would corrupt the context of FullFAT
538 if(pIoman->pBlkDevice->fnReadBlocks) {
539 return FF_ERR_IOMAN_DEV_ALREADY_REGD;
540 }
541 if(pIoman->pBlkDevice->fnWriteBlocks) {
542 return FF_ERR_IOMAN_DEV_ALREADY_REGD;
543 }
544 if(pIoman->pBlkDevice->pParam) {
545 return FF_ERR_IOMAN_DEV_ALREADY_REGD;
546 }
547
548 // Here we shall just set the values.
549 // FullFAT checks before using any of these values.
550 pIoman->pBlkDevice->devBlkSize = BlkSize;
551 pIoman->pBlkDevice->fnReadBlocks = fnReadBlocks;
552 pIoman->pBlkDevice->fnWriteBlocks = fnWriteBlocks;
553 pIoman->pBlkDevice->pParam = pParam;
554
555 return FF_ERR_NONE; // Success
556 }
557
558 /**
559 * @private
560 **/
561 static FF_ERROR FF_DetermineFatType(FF_IOMAN *pIoman) {
562
563 FF_PARTITION *pPart;
564 FF_BUFFER *pBuffer;
565 FF_T_UINT32 testLong;
566 if(pIoman) {
567 pPart = pIoman->pPartition;
568
569 if(pPart->NumClusters < 4085) {
570 // FAT12
571 pPart->Type = FF_T_FAT12;
572 #ifdef FF_FAT_CHECK
573 #ifdef FF_FAT12_SUPPORT
574 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA, FF_MODE_READ);
575 {
576 if(!pBuffer) {
577 return FF_ERR_DEVICE_DRIVER_FAILED;
578 }
579 testLong = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, 0x0000);
580 }
581 FF_ReleaseBuffer(pIoman, pBuffer);
582 if((testLong & 0x3FF) != 0x3F8) {
583 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
584 }
585 #else
586 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
587 #endif
588 #endif
589 #ifdef FF_FAT12_SUPPORT
590 return FF_ERR_NONE;
591 #endif
592
593 } else if(pPart->NumClusters < 65525) {
594 // FAT 16
595 pPart->Type = FF_T_FAT16;
596 #ifdef FF_FAT_CHECK
597 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA, FF_MODE_READ);
598 {
599 if(!pBuffer) {
600 return FF_ERR_DEVICE_DRIVER_FAILED;
601 }
602 testLong = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, 0x0000);
603 }
604 FF_ReleaseBuffer(pIoman, pBuffer);
605 if(testLong != 0xFFF8) {
606 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
607 }
608 #endif
609 return FF_ERR_NONE;
610 }
611 else {
612 // FAT 32!
613 pPart->Type = FF_T_FAT32;
614 #ifdef FF_FAT_CHECK
615 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA, FF_MODE_READ);
616 {
617 if(!pBuffer) {
618 return FF_ERR_DEVICE_DRIVER_FAILED;
619 }
620 testLong = FF_getLong(pBuffer->pBuffer, 0x0000);
621 }
622 FF_ReleaseBuffer(pIoman, pBuffer);
623 if((testLong & 0x0FFFFFF8) != 0x0FFFFFF8) {
624 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
625 }
626 #endif
627 return FF_ERR_NONE;
628 }
629 }
630
631 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
632 }
633 /**
634 * @public
635 * @brief Mounts the Specified partition, the volume specified by the FF_IOMAN object provided.
636 *
637 * The device drivers must adhere to the specification provided by
638 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
639 *
640 * @param pIoman FF_IOMAN object.
641 * @param PartitionNumber The primary partition number to be mounted. (0 - 3).
642 *
643 * @return 0 on success.
644 * @return FF_ERR_NULL_POINTER if a pIoman object wasn't provided.
645 * @return FF_ERR_IOMAN_INVALID_PARTITION_NUM if the partition number is out of range.
646 * @return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION if no partition was found.
647 * @return FF_ERR_IOMAN_INVALID_FORMAT if the master boot record or partition boot block didn't provide sensible data.
648 * @return FF_ERR_IOMAN_NOT_FAT_FORMATTED if the volume or partition couldn't be determined to be FAT. (@see ff_config.h)
649 *
650 **/
651 FF_ERROR FF_MountPartition(FF_IOMAN *pIoman, FF_T_UINT8 PartitionNumber) {
652 FF_PARTITION *pPart;
653 FF_BUFFER *pBuffer = 0;
654
655 if(!pIoman) {
656 return FF_ERR_NULL_POINTER;
657 }
658
659 if(PartitionNumber > 3) {
660 return FF_ERR_IOMAN_INVALID_PARTITION_NUM;
661 }
662
663 pPart = pIoman->pPartition;
664
665 pBuffer = FF_GetBuffer(pIoman, 0, FF_MODE_READ);
666 if(!pBuffer) {
667 return FF_ERR_DEVICE_DRIVER_FAILED;
668 }
669 pPart->BlkSize = FF_getShort(pBuffer->pBuffer, FF_FAT_BYTES_PER_SECTOR);
670
671 if((pPart->BlkSize % 512) == 0 && pPart->BlkSize > 0) {
672 // Volume is not partitioned (MBR Found)
673 pPart->BeginLBA = 0;
674 } else {
675 // Primary Partitions to deal with!
676 pPart->BeginLBA = FF_getLong(pBuffer->pBuffer, (FF_T_UINT16)(FF_FAT_PTBL + FF_FAT_PTBL_LBA + (16 * PartitionNumber)));
677 FF_ReleaseBuffer(pIoman, pBuffer);
678
679 if(!pPart->BeginLBA) {
680 return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION;
681 }
682 // Now we get the Partition sector.
683 pBuffer = FF_GetBuffer(pIoman, pPart->BeginLBA, FF_MODE_READ);
684 if(!pBuffer) {
685 return FF_ERR_DEVICE_DRIVER_FAILED;
686 }
687 pPart->BlkSize = FF_getShort(pBuffer->pBuffer, FF_FAT_BYTES_PER_SECTOR);
688 if((pPart->BlkSize % 512) != 0 || pPart->BlkSize == 0) {
689 FF_ReleaseBuffer(pIoman, pBuffer);
690 return FF_ERR_IOMAN_INVALID_FORMAT;
691 }
692 }
693 // Assume FAT16, then we'll adjust if its FAT32
694 pPart->ReservedSectors = FF_getShort(pBuffer->pBuffer, FF_FAT_RESERVED_SECTORS);
695 pPart->FatBeginLBA = pPart->BeginLBA + pPart->ReservedSectors;
696
697 pPart->NumFATS = (FF_T_UINT8) FF_getShort(pBuffer->pBuffer, FF_FAT_NUMBER_OF_FATS);
698 pPart->SectorsPerFAT = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, FF_FAT_16_SECTORS_PER_FAT);
699
700 pPart->SectorsPerCluster = FF_getChar(pBuffer->pBuffer, FF_FAT_SECTORS_PER_CLUS);
701
702 pPart->BlkFactor = (FF_T_UINT8) (pPart->BlkSize / pIoman->BlkSize); // Set the BlockFactor (How many real-blocks in a fake block!).
703
704 if(pPart->SectorsPerFAT == 0) { // FAT32
705 pPart->SectorsPerFAT = FF_getLong(pBuffer->pBuffer, FF_FAT_32_SECTORS_PER_FAT);
706 pPart->RootDirCluster = FF_getLong(pBuffer->pBuffer, FF_FAT_ROOT_DIR_CLUSTER);
707 pPart->ClusterBeginLBA = pPart->BeginLBA + pPart->ReservedSectors + (pPart->NumFATS * pPart->SectorsPerFAT);
708 pPart->TotalSectors = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, FF_FAT_16_TOTAL_SECTORS);
709 if(pPart->TotalSectors == 0) {
710 pPart->TotalSectors = FF_getLong(pBuffer->pBuffer, FF_FAT_32_TOTAL_SECTORS);
711 }
712 } else { // FAT16
713 pPart->ClusterBeginLBA = pPart->BeginLBA + pPart->ReservedSectors + (pPart->NumFATS * pPart->SectorsPerFAT);
714 pPart->TotalSectors = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, FF_FAT_16_TOTAL_SECTORS);
715 pPart->RootDirCluster = 1; // 1st Cluster is RootDir!
716 if(pPart->TotalSectors == 0) {
717 pPart->TotalSectors = FF_getLong(pBuffer->pBuffer, FF_FAT_32_TOTAL_SECTORS);
718 }
719 }
720
721 FF_ReleaseBuffer(pIoman, pBuffer); // Release the buffer finally!
722 pPart->RootDirSectors = ((FF_getShort(pBuffer->pBuffer, FF_FAT_ROOT_ENTRY_COUNT) * 32) + pPart->BlkSize - 1) / pPart->BlkSize;
723 pPart->FirstDataSector = pPart->ClusterBeginLBA + pPart->RootDirSectors;
724 pPart->DataSectors = pPart->TotalSectors - (pPart->ReservedSectors + (pPart->NumFATS * pPart->SectorsPerFAT) + pPart->RootDirSectors);
725 pPart->NumClusters = pPart->DataSectors / pPart->SectorsPerCluster;
726
727 if(FF_DetermineFatType(pIoman)) {
728 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
729 }
730
731 #ifdef FF_MOUNT_FIND_FREE
732 pPart->LastFreeCluster = FF_FindFreeCluster(pIoman);
733 pPart->FreeClusterCount = FF_CountFreeClusters(pIoman);
734 #else
735 pPart->LastFreeCluster = 0;
736 pPart->FreeClusterCount = 0;
737 #endif
738
739 return FF_ERR_NONE;
740 }
741
742 /**
743 * @public
744 * @brief Unregister a Blockdevice, so that the IOMAN can be re-used for another device.
745 *
746 * Any active partitions must be Unmounted first.
747 *
748 * @param pIoman FF_IOMAN object.
749 *
750 * @return FF_ERR_NONE on success.
751 **/
752 FF_ERROR FF_UnregisterBlkDevice(FF_IOMAN *pIoman) {
753
754 FF_T_SINT8 RetVal = FF_ERR_NONE;
755
756 if(!pIoman) {
757 return FF_ERR_NULL_POINTER;
758 }
759
760 FF_PendSemaphore(pIoman->pSemaphore);
761 {
762 if(pIoman->pPartition->PartitionMounted == FF_FALSE) {
763 pIoman->pBlkDevice->devBlkSize = 0;
764 pIoman->pBlkDevice->fnReadBlocks = NULL;
765 pIoman->pBlkDevice->fnWriteBlocks = NULL;
766 pIoman->pBlkDevice->pParam = NULL;
767 } else {
768 RetVal = FF_ERR_IOMAN_PARTITION_MOUNTED;
769 }
770 }
771 FF_ReleaseSemaphore(pIoman->pSemaphore);
772
773 return RetVal;
774 }
775
776 /**
777 * @private
778 * @brief Checks the cache for Active Handles
779 *
780 * @param pIoman FF_IOMAN Object.
781 *
782 * @return FF_TRUE if an active handle is found, else FF_FALSE.
783 *
784 * @pre This function must be wrapped with the cache handling semaphore.
785 **/
786 static FF_T_BOOL FF_ActiveHandles(FF_IOMAN *pIoman) {
787 FF_T_UINT32 i;
788 FF_BUFFER *pBuffer;
789
790 for(i = 0; i < pIoman->CacheSize; i++) {
791 pBuffer = (pIoman->pBuffers + i);
792 if(pBuffer->NumHandles) {
793 return FF_TRUE;
794 }
795 }
796
797 return FF_FALSE;
798 }
799
800
801 /**
802 * @public
803 * @brief Unmounts the active partition.
804 *
805 * @param pIoman FF_IOMAN Object.
806 *
807 * @return FF_ERR_NONE on success.
808 **/
809 FF_ERROR FF_UnmountPartition(FF_IOMAN *pIoman) {
810 FF_T_SINT8 RetVal = FF_ERR_NONE;
811
812 if(!pIoman) {
813 return FF_ERR_NULL_POINTER;
814 }
815
816 FF_PendSemaphore(pIoman->pSemaphore); // Ensure that there are no File Handles
817 {
818 if(!FF_ActiveHandles(pIoman)) {
819 if(pIoman->FirstFile == NULL) {
820 FF_FlushCache(pIoman); // Flush any unwritten sectors to disk.
821 pIoman->pPartition->PartitionMounted = FF_FALSE;
822 } else {
823 RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES;
824 }
825 } else {
826 RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES; // Active handles found on the cache.
827 }
828 }
829 FF_ReleaseSemaphore(pIoman->pSemaphore);
830
831 return RetVal;
832 }
833
834
835 FF_ERROR FF_IncreaseFreeClusters(FF_IOMAN *pIoman, FF_T_UINT32 Count) {
836
837 //FF_PendSemaphore(pIoman->pSemaphore);
838 //{
839 if(!pIoman->pPartition->FreeClusterCount) {
840 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
841 }
842 pIoman->pPartition->FreeClusterCount += Count;
843 //}
844 //FF_ReleaseSemaphore(pIoman->pSemaphore);
845
846 return FF_ERR_NONE;
847 }
848
849 FF_ERROR FF_DecreaseFreeClusters(FF_IOMAN *pIoman, FF_T_UINT32 Count) {
850
851 //FF_lockFAT(pIoman);
852 //{
853 if(!pIoman->pPartition->FreeClusterCount) {
854 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
855 }
856 pIoman->pPartition->FreeClusterCount -= Count;
857 //}
858 //FF_unlockFAT(pIoman);
859
860 return FF_ERR_NONE;
861 }
862
863
864 /**
865 * @brief Returns the Block-size of a mounted Partition
866 *
867 * The purpose of this function is to provide API access to information
868 * that might be useful in special cases. Like USB sticks that require a sector
869 * knocking sequence for security. After the sector knock, some secure USB
870 * sticks then present a different BlockSize.
871 *
872 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
873 *
874 * @return The blocksize of the partition. A value less than 0 when an error occurs.
875 * @return Any negative value can be cast to the FF_ERROR type.
876 **/
877 FF_T_SINT32 FF_GetPartitionBlockSize(FF_IOMAN *pIoman) {
878
879 if(pIoman) {
880 return (FF_T_SINT32) pIoman->pPartition->BlkSize;
881 }
882
883 return FF_ERR_NULL_POINTER;
884 }
885
886 #ifdef FF_64_NUM_SUPPORT
887 /**
888 * @brief Returns the number of bytes contained within the mounted partition or volume.
889 *
890 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
891 *
892 * @return The total number of bytes that the mounted partition or volume contains.
893 *
894 **/
895 FF_T_UINT64 FF_GetVolumeSize(FF_IOMAN *pIoman) {
896 if(pIoman) {
897 FF_T_UINT32 TotalClusters = pIoman->pPartition->DataSectors / pIoman->pPartition->SectorsPerCluster;
898 return (FF_T_UINT64) ((FF_T_UINT64)TotalClusters * (FF_T_UINT64)((FF_T_UINT64)pIoman->pPartition->SectorsPerCluster * (FF_T_UINT64)pIoman->pPartition->BlkSize));
899 }
900 return 0;
901 }
902
903 #else
904 FF_T_UINT32 FF_GetVolumeSize(FF_IOMAN *pIoman) {
905 FF_T_UINT32 TotalClusters = pIoman->pPartition->DataSectors / pIoman->pPartition->SectorsPerCluster;
906 return (FF_T_UINT32) (TotalClusters * (pIoman->pPartition->SectorsPerCluster * pIoman->pPartition->BlkSize));
907 }
908 #endif
909