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.
47 #include "ff_ioman.h" // Includes ff_types.h, ff_safety.h, <stdio.h>
48 #include "ff_fatdef.h"
50 extern FF_T_UINT32
FF_FindFreeCluster (FF_IOMAN
*pIoman
);
51 extern FF_T_UINT32
FF_CountFreeClusters (FF_IOMAN
*pIoman
);
53 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN
*pIoman
);
57 * @brief Creates an FF_IOMAN object, to initialise FullFAT
59 * @param pCacheMem Pointer to a buffer for the cache. (NULL if ok to Malloc).
60 * @param Size The size of the provided buffer, or size of the cache to be created.
61 * @param BlkSize The block size of devices to be attached. If in doubt use 512.
62 * @param pError Pointer to a signed byte for error checking. Can be NULL if not required.
63 * @param pError To be checked when a NULL pointer is returned.
65 * @return Returns a pointer to an FF_IOMAN type object. NULL on Error, check the contents of
68 FF_IOMAN
*FF_CreateIOMAN(FF_T_UINT8
*pCacheMem
, FF_T_UINT32 Size
, FF_T_UINT16 BlkSize
, FF_ERROR
*pError
) {
70 FF_IOMAN
*pIoman
= NULL
;
71 FF_T_UINT32
*pLong
= NULL
; // Force malloc to malloc memory on a 32-bit boundary.
77 *pError
= FF_ERR_NONE
;
80 if((BlkSize
% 512) != 0 || Size
== 0) {
82 *pError
= FF_ERR_IOMAN_BAD_BLKSIZE
;
84 return NULL
; // BlkSize Size not a multiple of 512 > 0
87 if((Size
% BlkSize
) != 0 || Size
== 0) {
89 *pError
= FF_ERR_IOMAN_BAD_MEMSIZE
;
91 return NULL
; // Memory Size not a multiple of BlkSize > 0
94 pIoman
= (FF_IOMAN
*) FF_MALLOC(sizeof(FF_IOMAN
));
96 if(!pIoman
) { // Ensure malloc() succeeded.
98 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
103 memset (pIoman
, '\0', sizeof(FF_IOMAN
));
105 // This is just a bit-mask, to use a byte to keep track of memory.
106 // pIoman->MemAllocation = 0x00; // Unset all allocation identifiers.
107 pIoman
->pPartition
= (FF_PARTITION
*) FF_MALLOC(sizeof(FF_PARTITION
));
108 if(!pIoman
->pPartition
) {
110 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
112 FF_DestroyIOMAN(pIoman
);
115 memset (pIoman
->pPartition
, '\0', sizeof(FF_PARTITION
));
117 pIoman
->MemAllocation
|= FF_IOMAN_ALLOC_PART
; // If succeeded, flag that allocation.
118 pIoman
->pPartition
->LastFreeCluster
= 0;
119 pIoman
->pPartition
->PartitionMounted
= FF_FALSE
; // This should be checked by FF_Open();
121 pIoman
->pPartition
->PCIndex
= 0;
122 for(i
= 0; i
< FF_PATH_CACHE_DEPTH
; i
++) {
123 pIoman
->pPartition
->PathCache
[i
].DirCluster
= 0;
124 pIoman
->pPartition
->PathCache
[i
].Path
[0] = '\0';
125 #ifdef FF_HASH_TABLE_SUPPORT
126 pIoman
->pPartition
->PathCache
[i
].pHashTable
= FF_CreateHashTable();
127 pIoman
->pPartition
->PathCache
[i
].bHashed
= FF_FALSE
;
132 pIoman
->pBlkDevice
= (FF_BLK_DEVICE
*) FF_MALLOC(sizeof(FF_BLK_DEVICE
));
133 if(!pIoman
->pBlkDevice
) { // If succeeded, flag that allocation.
135 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
137 FF_DestroyIOMAN(pIoman
);
140 memset (pIoman
->pBlkDevice
, '\0', sizeof(FF_BLK_DEVICE
));
141 pIoman
->MemAllocation
|= FF_IOMAN_ALLOC_BLKDEV
;
143 // Make sure all pointers are NULL
144 pIoman
->pBlkDevice
->fnReadBlocks
= NULL
;
145 pIoman
->pBlkDevice
->fnWriteBlocks
= NULL
;
146 pIoman
->pBlkDevice
->pParam
= NULL
;
148 // Organise the memory provided, or create our own!
150 pIoman
->pCacheMem
= pCacheMem
;
151 }else { // No-Cache buffer provided (malloc)
152 pLong
= (FF_T_UINT32
*) FF_MALLOC(Size
);
153 pIoman
->pCacheMem
= (FF_T_UINT8
*) pLong
;
154 if(!pIoman
->pCacheMem
) {
156 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
158 FF_DestroyIOMAN(pIoman
);
161 pIoman
->MemAllocation
|= FF_IOMAN_ALLOC_BUFFERS
;
164 memset (pIoman
->pCacheMem
, '\0', Size
);
166 pIoman
->BlkSize
= BlkSize
;
167 pIoman
->CacheSize
= (FF_T_UINT16
) (Size
/ BlkSize
);
168 pIoman
->FirstFile
= NULL
;
171 /* Malloc() memory for buffer objects. (FullFAT never refers to a buffer directly
172 but uses buffer objects instead. Allows us to provide thread safety.
174 pIoman
->pBuffers
= (FF_BUFFER
*) FF_MALLOC(sizeof(FF_BUFFER
) * pIoman
->CacheSize
);
176 if(!pIoman
->pBuffers
) {
178 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
180 FF_DestroyIOMAN(pIoman
);
181 return NULL
; // HT added
183 memset (pIoman
->pBuffers
, '\0', sizeof(FF_BUFFER
) * pIoman
->CacheSize
);
185 pIoman
->MemAllocation
|= FF_IOMAN_ALLOC_BUFDESCR
;
186 FF_IOMAN_InitBufferDescriptors(pIoman
);
188 // Finally create a Semaphore for Buffer Description modifications.
189 pIoman
->pSemaphore
= FF_CreateSemaphore();
191 return pIoman
; // Sucess, return the created object.
196 * @brief Destroys an FF_IOMAN object, and frees all assigned memory.
198 * @param pIoman Pointer to an FF_IOMAN object, as returned from FF_CreateIOMAN.
200 * @return FF_ERR_NONE on sucess, or a documented error code on failure. (FF_ERR_NULL_POINTER)
203 FF_ERROR
FF_DestroyIOMAN(FF_IOMAN
*pIoman
) {
205 // Ensure no NULL pointer was provided.
207 return FF_ERR_NULL_POINTER
;
210 // Ensure pPartition pointer was allocated.
211 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_PART
)) {
212 FF_FREE(pIoman
->pPartition
);
215 // Ensure pBlkDevice pointer was allocated.
216 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BLKDEV
)) {
217 FF_FREE(pIoman
->pBlkDevice
);
220 // Ensure pBuffers pointer was allocated.
221 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BUFDESCR
)) {
222 FF_FREE(pIoman
->pBuffers
);
225 // Ensure pCacheMem pointer was allocated.
226 if((pIoman
->MemAllocation
& FF_IOMAN_ALLOC_BUFFERS
)) {
227 FF_FREE(pIoman
->pCacheMem
);
230 // Destroy any Semaphore that was created.
231 if(pIoman
->pSemaphore
) {
232 FF_DestroySemaphore(pIoman
->pSemaphore
);
235 // Finally free the FF_IOMAN object.
243 * @brief Initialises Buffer Descriptions as part of the FF_IOMAN object initialisation.
245 * @param pIoman IOMAN Object.
248 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN
*pIoman
) {
250 FF_BUFFER
*pBuffer
= pIoman
->pBuffers
;
251 pIoman
->LastReplaced
= 0;
252 // HT : it is assmued that pBuffer was cleared by memset ()
253 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
254 pBuffer
->pBuffer
= (FF_T_UINT8
*)((pIoman
->pCacheMem
) + (pIoman
->BlkSize
* i
));
261 * @brief Tests the Mode for validity.
263 * @param Mode Mode of buffer to check.
265 * @return FF_TRUE when valid, else FF_FALSE.
267 /*static FF_T_BOOL FF_IOMAN_ModeValid(FF_T_UINT8 Mode) {
268 if(Mode == FF_MODE_READ || Mode == FF_MODE_WRITE) {
277 * @brief Fills a buffer with the appropriate sector via the device driver.
279 * @param pIoman FF_IOMAN object.
280 * @param Sector LBA address of the sector to fetch.
281 * @param pBuffer Pointer to a byte-wise buffer to store the fetched data.
283 * HT Note: will be called while semaphore claimed (by FF_GetBuffer)
285 * @return FF_TRUE when valid, else FF_FALSE.
287 static FF_ERROR
FF_IOMAN_FillBuffer(FF_IOMAN
*pIoman
, FF_T_UINT32 Sector
, FF_T_UINT8
*pBuffer
) {
288 FF_T_SINT32 retVal
= 0;
289 if(pIoman
->pBlkDevice
->fnReadBlocks
) { // Make sure we don't execute a NULL.
291 // Called from FF_GetBuffer with semaphore claimed
292 retVal
= pIoman
->pBlkDevice
->fnReadBlocks(pBuffer
, Sector
, 1, pIoman
->pBlkDevice
->pParam
);
293 if(retVal
== FF_ERR_DRIVER_BUSY
) {
294 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
296 } while(retVal
== FF_ERR_DRIVER_BUSY
);
298 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
301 return 0; // 1 Block was sucessfully read.
303 return -1; // 0 Blocks we're read, Error!
307 return -1; // error no device diver registered.
313 * @brief Flushes a buffer to the device driver.
315 * @param pIoman FF_IOMAN object.
316 * @param Sector LBA address of the sector to fetch.
317 * @param pBuffer Pointer to a byte-wise buffer to store the fetched data.
320 * HT made it a globally accesible function to be used by new module ff_format.c
321 * Note that this function is called when semaphore is already locked
323 * @return FF_TRUE when valid, else FF_FALSE.
325 /* static */ FF_ERROR
FF_IOMAN_FlushBuffer(FF_IOMAN
*pIoman
, FF_T_UINT32 Sector
, FF_T_UINT8
*pBuffer
) {
326 FF_T_SINT32 retVal
= 0;
327 if(pIoman
->pBlkDevice
->fnWriteBlocks
) { // Make sure we don't execute a NULL.
329 retVal
= pIoman
->pBlkDevice
->fnWriteBlocks(pBuffer
, Sector
, 1, pIoman
->pBlkDevice
->pParam
);
330 if(retVal
== FF_ERR_DRIVER_BUSY
) {
331 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
333 } while(retVal
== FF_ERR_DRIVER_BUSY
);
335 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
338 return FF_ERR_NONE
; // 1 Block was sucessfully written.
340 return -1; // 0 Blocks we're written, Error!
344 return -1; // error no device diver registered.
350 * @brief Flushes all Write cache buffers with no active Handles.
352 * @param pIoman IOMAN Object.
354 * @return FF_ERR_NONE on Success.
356 FF_ERROR
FF_FlushCache(FF_IOMAN
*pIoman
) {
361 return FF_ERR_NULL_POINTER
;
364 FF_PendSemaphore(pIoman
->pSemaphore
);
366 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
367 if((pIoman
->pBuffers
+ i
)->NumHandles
== 0 && (pIoman
->pBuffers
+ i
)->Modified
== FF_TRUE
) {
369 FF_IOMAN_FlushBuffer(pIoman
, (pIoman
->pBuffers
+ i
)->Sector
, (pIoman
->pBuffers
+ i
)->pBuffer
);
371 // Buffer has now been flushed, mark it as a read buffer and unmodified.
372 (pIoman
->pBuffers
+ i
)->Mode
= FF_MODE_READ
;
373 (pIoman
->pBuffers
+ i
)->Modified
= FF_FALSE
;
375 // Search for other buffers that used this sector, and mark them as modified
376 // So that further requests will result in the new sector being fetched.
377 for(x
= 0; x
< pIoman
->CacheSize
; x
++) {
379 if((pIoman
->pBuffers
+ x
)->Sector
== (pIoman
->pBuffers
+ i
)->Sector
&& (pIoman
->pBuffers
+ x
)->Mode
== FF_MODE_READ
) {
380 (pIoman
->pBuffers
+ x
)->Modified
= FF_TRUE
;
387 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
392 /*static FF_T_BOOL FF_isFATSector(FF_IOMAN *pIoman, FF_T_UINT32 Sector) {
393 if(Sector >= pIoman->pPartition->FatBeginLBA && Sector < (pIoman->pPartition->FatBeginLBA + pIoman->pPartition->ReservedSectors)) {
399 FF_BUFFER
*FF_GetBuffer(FF_IOMAN
*pIoman
, FF_T_UINT32 Sector
, FF_T_UINT8 Mode
) {
401 FF_BUFFER
*pBufLRU
= NULL
;
402 FF_BUFFER
*pBufLHITS
= NULL
;
403 FF_BUFFER
*pBufMatch
= NULL
;
408 FF_PendSemaphore(pIoman
->pSemaphore
);
410 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
411 pBuffer
= (pIoman
->pBuffers
+ i
);
412 if(pBuffer
->Sector
== Sector
&& pBuffer
->Valid
== FF_TRUE
) {
415 if(pBuffer
->NumHandles
== 0) {
425 if(pBuffer
->LRU
>= pBufLRU
->LRU
) {
426 if(pBuffer
->LRU
== pBufLRU
->LRU
) {
427 if(pBuffer
->Persistance
> pBufLRU
->Persistance
) {
435 if(pBuffer
->Persistance
< pBufLHITS
->Persistance
) {
443 // A Match was found process!
444 if(Mode
== FF_MODE_READ
&& pBufMatch
->Mode
== FF_MODE_READ
) {
445 pBufMatch
->NumHandles
+= 1;
446 pBufMatch
->Persistance
+= 1;
447 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
451 if(pBufMatch
->Mode
== FF_MODE_WRITE
&& pBufMatch
->NumHandles
== 0) { // This buffer has no attached handles.
452 pBufMatch
->Mode
= Mode
;
453 pBufMatch
->NumHandles
= 1;
454 pBufMatch
->Persistance
+= 1;
455 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
459 if(pBufMatch
->Mode
== FF_MODE_READ
&& Mode
== FF_MODE_WRITE
&& pBufMatch
->NumHandles
== 0) {
460 pBufMatch
->Mode
= Mode
;
461 pBufMatch
->Modified
= FF_TRUE
;
462 pBufMatch
->NumHandles
= 1;
463 pBufMatch
->Persistance
+= 1;
464 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
468 pBufMatch
= NULL
; // Sector is already in use, keep yielding until its available!
471 // Choose a suitable buffer!
473 // Process the suitable candidate.
474 if(pBufLRU
->Modified
== FF_TRUE
) {
475 FF_IOMAN_FlushBuffer(pIoman
, pBufLRU
->Sector
, pBufLRU
->pBuffer
);
477 pBufLRU
->Mode
= Mode
;
478 pBufLRU
->Persistance
= 1;
480 pBufLRU
->NumHandles
= 1;
481 pBufLRU
->Sector
= Sector
;
483 if(Mode
== FF_MODE_WRITE
) {
484 pBufLRU
->Modified
= FF_TRUE
;
486 pBufLRU
->Modified
= FF_FALSE
;
489 FF_IOMAN_FillBuffer(pIoman
, Sector
, pBufLRU
->pBuffer
);
490 pBufLRU
->Valid
= FF_TRUE
;
491 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
497 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
498 FF_Yield(); // Better to go asleep to give low-priority task a chance to release buffer(s)
501 return pBufMatch
; // Return the Matched Buffer!
507 * @brief Releases a buffer resource.
509 * @param pIoman Pointer to an FF_IOMAN object.
510 * @param pBuffer Pointer to an FF_BUFFER object.
513 void FF_ReleaseBuffer(FF_IOMAN
*pIoman
, FF_BUFFER
*pBuffer
) {
514 // Protect description changes with a semaphore.
515 FF_PendSemaphore(pIoman
->pSemaphore
);
517 if (pBuffer
->NumHandles
) {
518 pBuffer
->NumHandles
--;
520 //printf ("FF_ReleaseBuffer: buffer not claimed\n");
523 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
528 * @brief Registers a device driver with FullFAT
530 * The device drivers must adhere to the specification provided by
531 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
533 * @param pIoman FF_IOMAN object.
534 * @param BlkSize Block Size that the driver deals in. (Minimum 512, larger values must be a multiple of 512).
535 * @param fnWriteBlocks Pointer to the Write Blocks to device function, as described by FF_WRITE_BLOCKS.
536 * @param fnReadBlocks Pointer to the Read Blocks from device function, as described by FF_READ_BLOCKS.
537 * @param pParam Pointer to a parameter for use in the functions.
539 * @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.
541 FF_ERROR
FF_RegisterBlkDevice(FF_IOMAN
*pIoman
, FF_T_UINT16 BlkSize
, FF_WRITE_BLOCKS fnWriteBlocks
, FF_READ_BLOCKS fnReadBlocks
, void *pParam
) {
542 if(!pIoman
) { // We can't do anything without an IOMAN object.
543 return FF_ERR_NULL_POINTER
;
546 if((BlkSize
% 512) != 0 || BlkSize
== 0) {
547 return FF_ERR_IOMAN_DEV_INVALID_BLKSIZE
; // BlkSize Size not a multiple of IOMAN's Expected BlockSize > 0
550 if((BlkSize
% pIoman
->BlkSize
) != 0 || BlkSize
== 0) {
551 return FF_ERR_IOMAN_DEV_INVALID_BLKSIZE
; // BlkSize Size not a multiple of IOMAN's Expected BlockSize > 0
554 // Ensure that a device cannot be re-registered "mid-flight"
555 // Doing so would corrupt the context of FullFAT
556 if(pIoman
->pBlkDevice
->fnReadBlocks
) {
557 return FF_ERR_IOMAN_DEV_ALREADY_REGD
;
559 if(pIoman
->pBlkDevice
->fnWriteBlocks
) {
560 return FF_ERR_IOMAN_DEV_ALREADY_REGD
;
562 if(pIoman
->pBlkDevice
->pParam
) {
563 return FF_ERR_IOMAN_DEV_ALREADY_REGD
;
566 // Here we shall just set the values.
567 // FullFAT checks before using any of these values.
568 pIoman
->pBlkDevice
->devBlkSize
= BlkSize
;
569 pIoman
->pBlkDevice
->fnReadBlocks
= fnReadBlocks
;
570 pIoman
->pBlkDevice
->fnWriteBlocks
= fnWriteBlocks
;
571 pIoman
->pBlkDevice
->pParam
= pParam
;
573 return FF_ERR_NONE
; // Success
579 static FF_ERROR
FF_DetermineFatType(FF_IOMAN
*pIoman
) {
583 FF_T_UINT32 testLong
;
585 pPart
= pIoman
->pPartition
;
587 if(pPart
->NumClusters
< 4085) {
589 pPart
->Type
= FF_T_FAT12
;
591 #ifdef FF_FAT12_SUPPORT
592 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
595 return FF_ERR_DEVICE_DRIVER_FAILED
;
597 testLong
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, 0x0000);
599 FF_ReleaseBuffer(pIoman
, pBuffer
);
600 if((testLong
& 0x3FF) != 0x3F8) {
601 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
604 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
607 #ifdef FF_FAT12_SUPPORT
611 } else if(pPart
->NumClusters
< 65525) {
613 pPart
->Type
= FF_T_FAT16
;
615 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
618 return FF_ERR_DEVICE_DRIVER_FAILED
;
620 testLong
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, 0x0000);
622 FF_ReleaseBuffer(pIoman
, pBuffer
);
623 if(testLong
!= 0xFFF8) {
624 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
631 pPart
->Type
= FF_T_FAT32
;
633 pBuffer
= FF_GetBuffer(pIoman
, pIoman
->pPartition
->FatBeginLBA
, FF_MODE_READ
);
636 return FF_ERR_DEVICE_DRIVER_FAILED
;
638 testLong
= FF_getLong(pBuffer
->pBuffer
, 0x0000);
640 FF_ReleaseBuffer(pIoman
, pBuffer
);
641 if((testLong
& 0x0FFFFFF8) != 0x0FFFFFF8) {
642 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
649 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
652 static FF_T_SINT8
FF_PartitionCount (FF_T_UINT8
*pBuffer
)
654 FF_T_SINT8 count
= 0;
656 // Check PBR or MBR signature
657 if (FF_getChar(pBuffer
, FF_FAT_MBR_SIGNATURE
) != 0x55 &&
658 FF_getChar(pBuffer
, FF_FAT_MBR_SIGNATURE
) != 0xAA ) {
659 // No MBR, but is it a PBR ?
660 if (FF_getChar(pBuffer
, 0) == 0xEB && // PBR Byte 0
661 FF_getChar(pBuffer
, 2) == 0x90 && // PBR Byte 2
662 (FF_getChar(pBuffer
, 21) & 0xF0) == 0xF0) {// PBR Byte 21 : Media byte
663 return 1; // No MBR but PBR exist then only one partition
665 return 0; // No MBR and no PBR then no partition found
667 for (part
= 0; part
< 4; part
++) {
668 FF_T_UINT8 active
= FF_getChar(pBuffer
, FF_FAT_PTBL
+ FF_FAT_PTBL_ACTIVE
+ (16 * part
));
669 FF_T_UINT8 part_id
= FF_getChar(pBuffer
, FF_FAT_PTBL
+ FF_FAT_PTBL_ID
+ (16 * part
));
670 // The first sector must be a MBR, then check the partition entry in the MBR
671 if (active
!= 0x80 && (active
!= 0 || part_id
== 0)) {
681 * @brief Mounts the Specified partition, the volume specified by the FF_IOMAN object provided.
683 * The device drivers must adhere to the specification provided by
684 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
686 * @param pIoman FF_IOMAN object.
687 * @param PartitionNumber The primary partition number to be mounted. (0 - 3).
689 * @return 0 on success.
690 * @return FF_ERR_NULL_POINTER if a pIoman object wasn't provided.
691 * @return FF_ERR_IOMAN_INVALID_PARTITION_NUM if the partition number is out of range.
692 * @return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION if no partition was found.
693 * @return FF_ERR_IOMAN_INVALID_FORMAT if the master boot record or partition boot block didn't provide sensible data.
694 * @return FF_ERR_IOMAN_NOT_FAT_FORMATTED if the volume or partition couldn't be determined to be FAT. (@see ff_config.h)
697 FF_ERROR
FF_MountPartition(FF_IOMAN
*pIoman
, FF_T_UINT8 PartitionNumber
) {
699 FF_BUFFER
*pBuffer
= 0;
703 return FF_ERR_NULL_POINTER
;
706 if(PartitionNumber
> 3) {
707 return FF_ERR_IOMAN_INVALID_PARTITION_NUM
;
710 pPart
= pIoman
->pPartition
;
712 memset (pIoman
->pBuffers
, '\0', sizeof(FF_BUFFER
) * pIoman
->CacheSize
);
713 memset (pIoman
->pCacheMem
, '\0', pIoman
->BlkSize
* pIoman
->CacheSize
);
715 FF_IOMAN_InitBufferDescriptors(pIoman
);
716 pIoman
->FirstFile
= 0;
718 pBuffer
= FF_GetBuffer(pIoman
, 0, FF_MODE_READ
);
720 return FF_ERR_DEVICE_DRIVER_FAILED
;
723 partCount
= FF_PartitionCount (pBuffer
->pBuffer
);
725 pPart
->BlkSize
= FF_getShort(pBuffer
->pBuffer
, FF_FAT_BYTES_PER_SECTOR
);
727 if (partCount
== 0) { //(pPart->BlkSize % 512) == 0 && pPart->BlkSize > 0) {
728 // Volume is not partitioned (MBR Found)
731 // Primary Partitions to deal with!
732 pPart
->BeginLBA
= FF_getLong(pBuffer
->pBuffer
, FF_FAT_PTBL
+ FF_FAT_PTBL_LBA
+ (16 * PartitionNumber
));
733 FF_ReleaseBuffer(pIoman
, pBuffer
);
735 if(!pPart
->BeginLBA
) {
736 return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION
;
738 // Now we get the Partition sector.
739 pBuffer
= FF_GetBuffer(pIoman
, pPart
->BeginLBA
, FF_MODE_READ
);
741 return FF_ERR_DEVICE_DRIVER_FAILED
;
743 pPart
->BlkSize
= FF_getShort(pBuffer
->pBuffer
, FF_FAT_BYTES_PER_SECTOR
);
744 if((pPart
->BlkSize
% 512) != 0 || pPart
->BlkSize
== 0) {
745 FF_ReleaseBuffer(pIoman
, pBuffer
);
746 return FF_ERR_IOMAN_INVALID_FORMAT
;
750 // Assume FAT16, then we'll adjust if its FAT32
751 pPart
->ReservedSectors
= FF_getShort(pBuffer
->pBuffer
, FF_FAT_RESERVED_SECTORS
);
752 pPart
->FatBeginLBA
= pPart
->BeginLBA
+ pPart
->ReservedSectors
;
754 pPart
->NumFATS
= (FF_T_UINT8
) FF_getShort(pBuffer
->pBuffer
, FF_FAT_NUMBER_OF_FATS
);
755 pPart
->SectorsPerFAT
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, FF_FAT_16_SECTORS_PER_FAT
);
757 pPart
->SectorsPerCluster
= FF_getChar(pBuffer
->pBuffer
, FF_FAT_SECTORS_PER_CLUS
);
759 pPart
->BlkFactor
= (FF_T_UINT8
) (pPart
->BlkSize
/ pIoman
->BlkSize
); // Set the BlockFactor (How many real-blocks in a fake block!).
761 if(pPart
->SectorsPerFAT
== 0) { // FAT32
762 pPart
->SectorsPerFAT
= FF_getLong(pBuffer
->pBuffer
, FF_FAT_32_SECTORS_PER_FAT
);
763 pPart
->RootDirCluster
= FF_getLong(pBuffer
->pBuffer
, FF_FAT_ROOT_DIR_CLUSTER
);
764 pPart
->ClusterBeginLBA
= pPart
->BeginLBA
+ pPart
->ReservedSectors
+ (pPart
->NumFATS
* pPart
->SectorsPerFAT
);
765 pPart
->TotalSectors
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, FF_FAT_16_TOTAL_SECTORS
);
766 if(pPart
->TotalSectors
== 0) {
767 pPart
->TotalSectors
= FF_getLong(pBuffer
->pBuffer
, FF_FAT_32_TOTAL_SECTORS
);
769 memcpy (pPart
->VolLabel
, pBuffer
->pBuffer
+ FF_FAT_32_VOL_LABEL
, sizeof pPart
->VolLabel
);
771 pPart
->ClusterBeginLBA
= pPart
->BeginLBA
+ pPart
->ReservedSectors
+ (pPart
->NumFATS
* pPart
->SectorsPerFAT
);
772 pPart
->TotalSectors
= (FF_T_UINT32
) FF_getShort(pBuffer
->pBuffer
, FF_FAT_16_TOTAL_SECTORS
);
773 pPart
->RootDirCluster
= 1; // 1st Cluster is RootDir!
774 if(pPart
->TotalSectors
== 0) {
775 pPart
->TotalSectors
= FF_getLong(pBuffer
->pBuffer
, FF_FAT_32_TOTAL_SECTORS
);
777 memcpy (pPart
->VolLabel
, pBuffer
->pBuffer
+ FF_FAT_16_VOL_LABEL
, sizeof pPart
->VolLabel
);
780 FF_ReleaseBuffer(pIoman
, pBuffer
); // Release the buffer finally!
781 pPart
->RootDirSectors
= ((FF_getShort(pBuffer
->pBuffer
, FF_FAT_ROOT_ENTRY_COUNT
) * 32) + pPart
->BlkSize
- 1) / pPart
->BlkSize
;
782 pPart
->FirstDataSector
= pPart
->ClusterBeginLBA
+ pPart
->RootDirSectors
;
783 pPart
->DataSectors
= pPart
->TotalSectors
- (pPart
->ReservedSectors
+ (pPart
->NumFATS
* pPart
->SectorsPerFAT
) + pPart
->RootDirSectors
);
784 pPart
->NumClusters
= pPart
->DataSectors
/ pPart
->SectorsPerCluster
;
786 if(FF_DetermineFatType(pIoman
)) {
787 return FF_ERR_IOMAN_NOT_FAT_FORMATTED
;
790 #ifdef FF_MOUNT_FIND_FREE
791 pPart
->LastFreeCluster
= FF_FindFreeCluster(pIoman
);
792 pPart
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
794 pPart
->LastFreeCluster
= 0;
795 pPart
->FreeClusterCount
= 0;
803 * @brief Unregister a Blockdevice, so that the IOMAN can be re-used for another device.
805 * Any active partitions must be Unmounted first.
807 * @param pIoman FF_IOMAN object.
809 * @return FF_ERR_NONE on success.
811 FF_ERROR
FF_UnregisterBlkDevice(FF_IOMAN
*pIoman
) {
813 FF_T_SINT8 RetVal
= FF_ERR_NONE
;
816 return FF_ERR_NULL_POINTER
;
819 FF_PendSemaphore(pIoman
->pSemaphore
);
821 if(pIoman
->pPartition
->PartitionMounted
== FF_FALSE
) {
822 pIoman
->pBlkDevice
->devBlkSize
= 0;
823 pIoman
->pBlkDevice
->fnReadBlocks
= NULL
;
824 pIoman
->pBlkDevice
->fnWriteBlocks
= NULL
;
825 pIoman
->pBlkDevice
->pParam
= NULL
;
827 RetVal
= FF_ERR_IOMAN_PARTITION_MOUNTED
;
830 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
837 * @brief Checks the cache for Active Handles
839 * @param pIoman FF_IOMAN Object.
841 * @return FF_TRUE if an active handle is found, else FF_FALSE.
843 * @pre This function must be wrapped with the cache handling semaphore.
845 static FF_T_BOOL
FF_ActiveHandles(FF_IOMAN
*pIoman
) {
849 for(i
= 0; i
< pIoman
->CacheSize
; i
++) {
850 pBuffer
= (pIoman
->pBuffers
+ i
);
851 if(pBuffer
->NumHandles
) {
862 * @brief Unmounts the active partition.
864 * @param pIoman FF_IOMAN Object.
866 * @return FF_ERR_NONE on success.
868 FF_ERROR
FF_UnmountPartition(FF_IOMAN
*pIoman
) {
869 FF_T_SINT8 RetVal
= FF_ERR_NONE
;
872 return FF_ERR_NULL_POINTER
;
875 FF_PendSemaphore(pIoman
->pSemaphore
); // Ensure that there are no File Handles
877 if(!FF_ActiveHandles(pIoman
)) {
878 if(pIoman
->FirstFile
== NULL
) {
879 FF_FlushCache(pIoman
); // Flush any unwritten sectors to disk.
880 pIoman
->pPartition
->PartitionMounted
= FF_FALSE
;
882 RetVal
= FF_ERR_IOMAN_ACTIVE_HANDLES
;
885 RetVal
= FF_ERR_IOMAN_ACTIVE_HANDLES
; // Active handles found on the cache.
888 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
894 FF_ERROR
FF_IncreaseFreeClusters(FF_IOMAN
*pIoman
, FF_T_UINT32 Count
) {
896 //FF_PendSemaphore(pIoman->pSemaphore);
898 if(!pIoman
->pPartition
->FreeClusterCount
) {
899 pIoman
->pPartition
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
901 pIoman
->pPartition
->FreeClusterCount
+= Count
;
903 //FF_ReleaseSemaphore(pIoman->pSemaphore);
908 FF_ERROR
FF_DecreaseFreeClusters(FF_IOMAN
*pIoman
, FF_T_UINT32 Count
) {
910 //FF_lockFAT(pIoman);
912 if(!pIoman
->pPartition
->FreeClusterCount
) {
913 pIoman
->pPartition
->FreeClusterCount
= FF_CountFreeClusters(pIoman
);
915 pIoman
->pPartition
->FreeClusterCount
-= Count
;
917 //FF_unlockFAT(pIoman);
924 * @brief Returns the Block-size of a mounted Partition
926 * The purpose of this function is to provide API access to information
927 * that might be useful in special cases. Like USB sticks that require a sector
928 * knocking sequence for security. After the sector knock, some secure USB
929 * sticks then present a different BlockSize.
931 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
933 * @return The blocksize of the partition. A value less than 0 when an error occurs.
934 * @return Any negative value can be cast to the FF_ERROR type.
936 FF_T_SINT32
FF_GetPartitionBlockSize(FF_IOMAN
*pIoman
) {
939 return (FF_T_SINT32
) pIoman
->pPartition
->BlkSize
;
942 return FF_ERR_NULL_POINTER
;
945 #ifdef FF_64_NUM_SUPPORT
947 * @brief Returns the number of bytes contained within the mounted partition or volume.
949 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
951 * @return The total number of bytes that the mounted partition or volume contains.
954 FF_T_UINT64
FF_GetVolumeSize(FF_IOMAN
*pIoman
) {
956 FF_T_UINT32 TotalClusters
= pIoman
->pPartition
->DataSectors
/ pIoman
->pPartition
->SectorsPerCluster
;
957 return (FF_T_UINT64
) ((FF_T_UINT64
)TotalClusters
* (FF_T_UINT64
)((FF_T_UINT64
)pIoman
->pPartition
->SectorsPerCluster
* (FF_T_UINT64
)pIoman
->pPartition
->BlkSize
));
963 FF_T_UINT32
FF_GetVolumeSize(FF_IOMAN
*pIoman
) {
964 FF_T_UINT32 TotalClusters
= pIoman
->pPartition
->DataSectors
/ pIoman
->pPartition
->SectorsPerCluster
;
965 return (FF_T_UINT32
) (TotalClusters
* (pIoman
->pPartition
->SectorsPerCluster
* pIoman
->pPartition
->BlkSize
));