1 /*****************************************************************************
2 * FullFAT - High Performance, Thread-Safe Embedded FAT File-System *
3 * Copyright (C) 2009 James Walmsley (james@worm.me.uk) *
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. *
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. *
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/>. *
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. *
24 * See RESTRICTIONS.TXT for extra restrictions on the use of FullFAT. *
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 *****************************************************************************/
34 * @author James Walmsley
37 * @defgroup IOMAN I/O Manager
38 * @brief Handles IO buffers for FullFAT safely.
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.
45 #include "ff_ioman.h" // Includes ff_types.h, ff_safety.h, <stdio.h>
48 extern FF_T_UINT32
FF_FindFreeCluster (FF_IOMAN
*pIoman
);
49 extern FF_T_UINT32
FF_CountFreeClusters (FF_IOMAN
*pIoman
);
51 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN
*pIoman
);
55 * @brief Creates an FF_IOMAN object, to initialise FullFAT
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.
63 * @return Returns a pointer to an FF_IOMAN type object. NULL on Error, check the contents of
66 FF_IOMAN
*FF_CreateIOMAN(FF_T_UINT8
*pCacheMem
, FF_T_UINT32 Size
, FF_T_UINT16 BlkSize
, FF_ERROR
*pError
) {
68 FF_IOMAN
*pIoman
= NULL
;
69 FF_T_UINT32
*pLong
= NULL
; // Force malloc to malloc memory on a 32-bit boundary.
75 *pError
= FF_ERR_NONE
;
78 if((BlkSize
% 512) != 0 || Size
== 0) {
80 *pError
= FF_ERR_IOMAN_BAD_BLKSIZE
;
82 return NULL
; // BlkSize Size not a multiple of 512 > 0
85 if((Size
% BlkSize
) != 0 || Size
== 0) {
87 *pError
= FF_ERR_IOMAN_BAD_MEMSIZE
;
89 return NULL
; // Memory Size not a multiple of BlkSize > 0
92 pIoman
= (FF_IOMAN
*) malloc(sizeof(FF_IOMAN
));
94 if(!pIoman
) { // Ensure malloc() succeeded.
96 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
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
;
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();
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
;
126 FF_DestroyIOMAN(pIoman
);
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
;
134 // Make sure all pointers are NULL
135 pIoman
->pBlkDevice
->fnReadBlocks
= NULL
;
136 pIoman
->pBlkDevice
->fnWriteBlocks
= NULL
;
137 pIoman
->pBlkDevice
->pParam
= NULL
;
140 FF_DestroyIOMAN(pIoman
);
144 // Organise the memory provided, or create our own!
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
);
158 pIoman
->BlkSize
= BlkSize
;
159 pIoman
->CacheSize
= (FF_T_UINT16
) (Size
/ BlkSize
);
160 pIoman
->FirstFile
= NULL
;
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.
166 pIoman
->pBuffers
= (FF_BUFFER
*) malloc(sizeof(FF_BUFFER
) * pIoman
->CacheSize
);
168 if(pIoman
->pBuffers
) {
169 pIoman
->MemAllocation
|= FF_IOMAN_ALLOC_BUFDESCR
;
170 FF_IOMAN_InitBufferDescriptors(pIoman
);
172 FF_DestroyIOMAN(pIoman
);
175 // Finally create a Semaphore for Buffer Description modifications.
176 pIoman
->pSemaphore
= FF_CreateSemaphore();
178 return pIoman
; // Sucess, return the created object.
183 * @brief Destroys an FF_IOMAN object, and frees all assigned memory.
185 * @param pIoman Pointer to an FF_IOMAN object, as returned from FF_CreateIOMAN.
187 * @return FF_ERR_NONE on sucess, or a documented error code on failure. (FF_ERR_NULL_POINTER)
190 FF_ERROR
FF_DestroyIOMAN(FF_IOMAN
*pIoman
) {
192 // Ensure no NULL pointer was provided.
194 return FF_ERR_NULL_POINTER
;
197 // Ensure pPartition pointer was allocated.
198 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_PART
)) {
199 free(pIoman
->pPartition
);
202 // Ensure pBlkDevice pointer was allocated.
203 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BLKDEV
)) {
204 free(pIoman
->pBlkDevice
);
207 // Ensure pBuffers pointer was allocated.
208 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BUFDESCR
)) {
209 free(pIoman
->pBuffers
);
212 // Ensure pCacheMem pointer was allocated.
213 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BUFFERS
)) {
214 free(pIoman
->pCacheMem
);
217 // Destroy any Semaphore that was created.
218 if(pIoman
->pSemaphore
) {
219 FF_DestroySemaphore(pIoman
->pSemaphore
);
222 // Finally free the FF_IOMAN object.
230 * @brief Initialises Buffer Descriptions as part of the FF_IOMAN object initialisation.
232 * @param pIoman IOMAN Object.
235 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN
*pIoman
) {
237 FF_BUFFER
*pBuffer
= pIoman
->pBuffers
;
238 pIoman
->LastReplaced
= 0;
239 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
241 pBuffer
->NumHandles
= 0;
242 pBuffer
->Persistance
= 0;
245 pBuffer
->pBuffer
= (FF_T_UINT8
*)((pIoman
->pCacheMem
) + (pIoman
->BlkSize
* i
));
246 pBuffer
->Modified
= FF_FALSE
;
247 pBuffer
->Valid
= FF_FALSE
;
254 * @brief Tests the Mode for validity.
256 * @param Mode Mode of buffer to check.
258 * @return FF_TRUE when valid, else FF_FALSE.
260 /*static FF_T_BOOL FF_IOMAN_ModeValid(FF_T_UINT8 Mode) {
261 if(Mode == FF_MODE_READ || Mode == FF_MODE_WRITE) {
270 * @brief Fills a buffer with the appropriate sector via the device driver.
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.
276 * @return FF_TRUE when valid, else FF_FALSE.
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.
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
);
286 } while(retVal
== FF_ERR_DRIVER_BUSY
);
288 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
291 return 0; // 1 Block was sucessfully read.
293 return -1; // 0 Blocks we're read, Error!
297 return -1; // error no device diver registered.
303 * @brief Flushes a buffer to the device driver.
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.
309 * @return FF_TRUE when valid, else FF_FALSE.
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.
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
);
319 } while(retVal
== FF_ERR_DRIVER_BUSY
);
321 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
324 return FF_ERR_NONE
; // 1 Block was sucessfully written.
326 return -1; // 0 Blocks we're written, Error!
330 return -1; // error no device diver registered.
336 * @brief Flushes all Write cache buffers with no active Handles.
338 * @param pIoman IOMAN Object.
340 * @return FF_ERR_NONE on Success.
342 FF_ERROR
FF_FlushCache(FF_IOMAN
*pIoman
) {
347 return FF_ERR_NULL_POINTER
;
350 FF_PendSemaphore(pIoman
->pSemaphore
);
352 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
353 if((pIoman
->pBuffers
+ i
)->NumHandles
== 0 && (pIoman
->pBuffers
+ i
)->Modified
== FF_TRUE
) {
355 FF_IOMAN_FlushBuffer(pIoman
, (pIoman
->pBuffers
+ i
)->Sector
, (pIoman
->pBuffers
+ i
)->pBuffer
);
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
;
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
++) {
365 if((pIoman
->pBuffers
+ x
)->Sector
== (pIoman
->pBuffers
+ i
)->Sector
&& (pIoman
->pBuffers
+ x
)->Mode
== FF_MODE_READ
) {
366 (pIoman
->pBuffers
+ x
)->Modified
= FF_TRUE
;
373 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
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)) {
385 FF_BUFFER
*FF_GetBuffer(FF_IOMAN
*pIoman
, FF_T_UINT32 Sector
, FF_T_UINT8 Mode
) {
387 FF_BUFFER
*pBufLRU
= NULL
;
388 FF_BUFFER
*pBufLHITS
= NULL
;
389 FF_BUFFER
*pBufMatch
= NULL
;
394 FF_PendSemaphore(pIoman
->pSemaphore
);
396 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
397 pBuffer
= (pIoman
->pBuffers
+ i
);
398 if(pBuffer
->Sector
== Sector
&& pBuffer
->Valid
== FF_TRUE
) {
401 if(pBuffer
->NumHandles
== 0) {
411 if(pBuffer
->LRU
>= pBufLRU
->LRU
) {
412 if(pBuffer
->LRU
== pBufLRU
->LRU
) {
413 if(pBuffer
->Persistance
> pBufLRU
->Persistance
) {
421 if(pBuffer
->Persistance
< pBufLHITS
->Persistance
) {
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
);
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
);
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
);
454 pBufMatch
= NULL
; // Sector is already in use, keep yielding until its available!
457 // Choose a suitable buffer!
459 // Process the suitable candidate.
460 if(pBufLRU
->Modified
== FF_TRUE
) {
461 FF_IOMAN_FlushBuffer(pIoman
, pBufLRU
->Sector
, pBufLRU
->pBuffer
);
463 pBufLRU
->Mode
= Mode
;
464 pBufLRU
->Persistance
= 1;
466 pBufLRU
->NumHandles
= 1;
467 pBufLRU
->Sector
= Sector
;
469 if(Mode
== FF_MODE_WRITE
) {
470 pBufLRU
->Modified
= FF_TRUE
;
472 pBufLRU
->Modified
= FF_FALSE
;
475 FF_IOMAN_FillBuffer(pIoman
, Sector
, pBufLRU
->pBuffer
);
476 pBufLRU
->Valid
= FF_TRUE
;
477 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
483 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
487 return pBufMatch
; // Return the Matched Buffer!
493 * @brief Releases a buffer resource.
495 * @param pIoman Pointer to an FF_IOMAN object.
496 * @param pBuffer Pointer to an FF_BUFFER object.
499 void FF_ReleaseBuffer(FF_IOMAN
*pIoman
, FF_BUFFER
*pBuffer
) {
500 // Protect description changes with a semaphore.
501 FF_PendSemaphore(pIoman
->pSemaphore
);
503 pBuffer
->NumHandles
--;
505 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
510 * @brief Registers a device driver with FullFAT
512 * The device drivers must adhere to the specification provided by
513 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
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.
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.
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
;
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
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
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
;
541 if(pIoman
->pBlkDevice
->fnWriteBlocks
) {
542 return FF_ERR_IOMAN_DEV_ALREADY_REGD
;
544 if(pIoman
->pBlkDevice
->pParam
) {
545 return FF_ERR_IOMAN_DEV_ALREADY_REGD
;
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
;
555 return FF_ERR_NONE
; // Success
561 static FF_ERROR
FF_DetermineFatType(FF_IOMAN
*pIoman
) {
565 FF_T_UINT32 testLong
;
567 pPart
= pIoman
->pPartition
;
569 if(pPart
->NumClusters
< 4085) {
571 pPart
->Type
= FF_T_FAT12
;
573 #ifdef FF_FAT12_SUPPORT
574 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
577 return FF_ERR_DEVICE_DRIVER_FAILED
;
579 testLong
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, 0x0000);
581 FF_ReleaseBuffer(pIoman
, pBuffer
);
582 if((testLong
& 0x3FF) != 0x3F8) {
583 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
586 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
589 #ifdef FF_FAT12_SUPPORT
593 } else if(pPart
->NumClusters
< 65525) {
595 pPart
->Type
= FF_T_FAT16
;
597 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
600 return FF_ERR_DEVICE_DRIVER_FAILED
;
602 testLong
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, 0x0000);
604 FF_ReleaseBuffer(pIoman
, pBuffer
);
605 if(testLong
!= 0xFFF8) {
606 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
613 pPart
->Type
= FF_T_FAT32
;
615 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
618 return FF_ERR_DEVICE_DRIVER_FAILED
;
620 testLong
= FF_getLong(pBuffer
->pBuffer
, 0x0000);
622 FF_ReleaseBuffer(pIoman
, pBuffer
);
623 if((testLong
& 0x0FFFFFF8) != 0x0FFFFFF8) {
624 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
631 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
635 * @brief Mounts the Specified partition, the volume specified by the FF_IOMAN object provided.
637 * The device drivers must adhere to the specification provided by
638 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
640 * @param pIoman FF_IOMAN object.
641 * @param PartitionNumber The primary partition number to be mounted. (0 - 3).
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)
651 FF_ERROR
FF_MountPartition(FF_IOMAN
*pIoman
, FF_T_UINT8 PartitionNumber
) {
653 FF_BUFFER
*pBuffer
= 0;
656 return FF_ERR_NULL_POINTER
;
659 if(PartitionNumber
> 3) {
660 return FF_ERR_IOMAN_INVALID_PARTITION_NUM
;
663 pPart
= pIoman
->pPartition
;
665 pBuffer
= FF_GetBuffer(pIoman
, 0, FF_MODE_READ
);
667 return FF_ERR_DEVICE_DRIVER_FAILED
;
669 pPart
->BlkSize
= FF_getShort(pBuffer
->pBuffer
, FF_FAT_BYTES_PER_SECTOR
);
671 if((pPart
->BlkSize
% 512) == 0 && pPart
->BlkSize
> 0) {
672 // Volume is not partitioned (MBR Found)
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
);
679 if(!pPart
->BeginLBA
) {
680 return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION
;
682 // Now we get the Partition sector.
683 pBuffer
= FF_GetBuffer(pIoman
, pPart
->BeginLBA
, FF_MODE_READ
);
685 return FF_ERR_DEVICE_DRIVER_FAILED
;
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
;
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
;
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
);
700 pPart
->SectorsPerCluster
= FF_getChar(pBuffer
->pBuffer
, FF_FAT_SECTORS_PER_CLUS
);
702 pPart
->BlkFactor
= (FF_T_UINT8
) (pPart
->BlkSize
/ pIoman
->BlkSize
); // Set the BlockFactor (How many real-blocks in a fake block!).
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
);
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
);
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
;
727 if(FF_DetermineFatType(pIoman
)) {
728 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
731 #ifdef FF_MOUNT_FIND_FREE
732 pPart
->LastFreeCluster
= FF_FindFreeCluster(pIoman
);
733 pPart
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
735 pPart
->LastFreeCluster
= 0;
736 pPart
->FreeClusterCount
= 0;
744 * @brief Unregister a Blockdevice, so that the IOMAN can be re-used for another device.
746 * Any active partitions must be Unmounted first.
748 * @param pIoman FF_IOMAN object.
750 * @return FF_ERR_NONE on success.
752 FF_ERROR
FF_UnregisterBlkDevice(FF_IOMAN
*pIoman
) {
754 FF_T_SINT8 RetVal
= FF_ERR_NONE
;
757 return FF_ERR_NULL_POINTER
;
760 FF_PendSemaphore(pIoman
->pSemaphore
);
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
;
768 RetVal
= FF_ERR_IOMAN_PARTITION_MOUNTED
;
771 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
778 * @brief Checks the cache for Active Handles
780 * @param pIoman FF_IOMAN Object.
782 * @return FF_TRUE if an active handle is found, else FF_FALSE.
784 * @pre This function must be wrapped with the cache handling semaphore.
786 static FF_T_BOOL
FF_ActiveHandles(FF_IOMAN
*pIoman
) {
790 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
791 pBuffer
= (pIoman
->pBuffers
+ i
);
792 if(pBuffer
->NumHandles
) {
803 * @brief Unmounts the active partition.
805 * @param pIoman FF_IOMAN Object.
807 * @return FF_ERR_NONE on success.
809 FF_ERROR
FF_UnmountPartition(FF_IOMAN
*pIoman
) {
810 FF_T_SINT8 RetVal
= FF_ERR_NONE
;
813 return FF_ERR_NULL_POINTER
;
816 FF_PendSemaphore(pIoman
->pSemaphore
); // Ensure that there are no File Handles
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
;
823 RetVal
= FF_ERR_IOMAN_ACTIVE_HANDLES
;
826 RetVal
= FF_ERR_IOMAN_ACTIVE_HANDLES
; // Active handles found on the cache.
829 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
835 FF_ERROR
FF_IncreaseFreeClusters(FF_IOMAN
*pIoman
, FF_T_UINT32 Count
) {
837 //FF_PendSemaphore(pIoman->pSemaphore);
839 if(!pIoman
->pPartition
->FreeClusterCount
) {
840 pIoman
->pPartition
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
842 pIoman
->pPartition
->FreeClusterCount
+= Count
;
844 //FF_ReleaseSemaphore(pIoman->pSemaphore);
849 FF_ERROR
FF_DecreaseFreeClusters(FF_IOMAN
*pIoman
, FF_T_UINT32 Count
) {
851 //FF_lockFAT(pIoman);
853 if(!pIoman
->pPartition
->FreeClusterCount
) {
854 pIoman
->pPartition
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
856 pIoman
->pPartition
->FreeClusterCount
-= Count
;
858 //FF_unlockFAT(pIoman);
865 * @brief Returns the Block-size of a mounted Partition
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.
872 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
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.
877 FF_T_SINT32
FF_GetPartitionBlockSize(FF_IOMAN
*pIoman
) {
880 return (FF_T_SINT32
) pIoman
->pPartition
->BlkSize
;
883 return FF_ERR_NULL_POINTER
;
886 #ifdef FF_64_NUM_SUPPORT
888 * @brief Returns the number of bytes contained within the mounted partition or volume.
890 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
892 * @return The total number of bytes that the mounted partition or volume contains.
895 FF_T_UINT64
FF_GetVolumeSize(FF_IOMAN
*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
));
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
));