{FULLFAT]
[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 <string.h>
46
47 #include "ff_ioman.h" // Includes ff_types.h, ff_safety.h, <stdio.h>
48 #include "ff_fatdef.h"
49
50 extern FF_T_UINT32 FF_FindFreeCluster (FF_IOMAN *pIoman);
51 extern FF_T_UINT32 FF_CountFreeClusters (FF_IOMAN *pIoman);
52
53 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN *pIoman);
54
55 /**
56 * @public
57 * @brief Creates an FF_IOMAN object, to initialise FullFAT
58 *
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.
64 *
65 * @return Returns a pointer to an FF_IOMAN type object. NULL on Error, check the contents of
66 * @return pError
67 **/
68 FF_IOMAN *FF_CreateIOMAN(FF_T_UINT8 *pCacheMem, FF_T_UINT32 Size, FF_T_UINT16 BlkSize, FF_ERROR *pError) {
69
70 FF_IOMAN *pIoman = NULL;
71 FF_T_UINT32 *pLong = NULL; // Force malloc to malloc memory on a 32-bit boundary.
72 #ifdef FF_PATH_CACHE
73 FF_T_UINT32 i;
74 #endif
75
76 if(pError) {
77 *pError = FF_ERR_NONE;
78 }
79
80 if((BlkSize % 512) != 0 || Size == 0) {
81 if(pError) {
82 *pError = FF_ERR_IOMAN_BAD_BLKSIZE;
83 }
84 return NULL; // BlkSize Size not a multiple of 512 > 0
85 }
86
87 if((Size % BlkSize) != 0 || Size == 0) {
88 if(pError) {
89 *pError = FF_ERR_IOMAN_BAD_MEMSIZE;
90 }
91 return NULL; // Memory Size not a multiple of BlkSize > 0
92 }
93
94 pIoman = (FF_IOMAN *) FF_MALLOC(sizeof(FF_IOMAN));
95
96 if(!pIoman) { // Ensure malloc() succeeded.
97 if(pError) {
98 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
99 }
100 return NULL;
101 }
102
103 memset (pIoman, '\0', sizeof(FF_IOMAN));
104
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) {
109 if(pError) {
110 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
111 }
112 FF_DestroyIOMAN(pIoman);
113 return NULL;
114 }
115 memset (pIoman->pPartition, '\0', sizeof(FF_PARTITION));
116
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();
120 #ifdef FF_PATH_CACHE
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;
128 #endif
129 }
130 #endif
131
132 pIoman->pBlkDevice = (FF_BLK_DEVICE *) FF_MALLOC(sizeof(FF_BLK_DEVICE));
133 if(!pIoman->pBlkDevice) { // If succeeded, flag that allocation.
134 if(pError) {
135 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
136 }
137 FF_DestroyIOMAN(pIoman);
138 return NULL;
139 }
140 memset (pIoman->pBlkDevice, '\0', sizeof(FF_BLK_DEVICE));
141 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BLKDEV;
142
143 // Make sure all pointers are NULL
144 pIoman->pBlkDevice->fnReadBlocks = NULL;
145 pIoman->pBlkDevice->fnWriteBlocks = NULL;
146 pIoman->pBlkDevice->pParam = NULL;
147
148 // Organise the memory provided, or create our own!
149 if(pCacheMem) {
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) {
155 if(pError) {
156 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
157 }
158 FF_DestroyIOMAN(pIoman);
159 return NULL;
160 }
161 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFFERS;
162
163 }
164 memset (pIoman->pCacheMem, '\0', Size);
165
166 pIoman->BlkSize = BlkSize;
167 pIoman->CacheSize = (FF_T_UINT16) (Size / BlkSize);
168 pIoman->FirstFile = NULL;
169 pIoman->Locks = 0;
170
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.
173 */
174 pIoman->pBuffers = (FF_BUFFER *) FF_MALLOC(sizeof(FF_BUFFER) * pIoman->CacheSize);
175
176 if(!pIoman->pBuffers) {
177 if(pError) {
178 *pError = FF_ERR_NOT_ENOUGH_MEMORY;
179 }
180 FF_DestroyIOMAN(pIoman);
181 return NULL; // HT added
182 }
183 memset (pIoman->pBuffers, '\0', sizeof(FF_BUFFER) * pIoman->CacheSize);
184
185 pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFDESCR;
186 FF_IOMAN_InitBufferDescriptors(pIoman);
187
188 // Finally create a Semaphore for Buffer Description modifications.
189 pIoman->pSemaphore = FF_CreateSemaphore();
190
191 return pIoman; // Sucess, return the created object.
192 }
193
194 /**
195 * @public
196 * @brief Destroys an FF_IOMAN object, and frees all assigned memory.
197 *
198 * @param pIoman Pointer to an FF_IOMAN object, as returned from FF_CreateIOMAN.
199 *
200 * @return FF_ERR_NONE on sucess, or a documented error code on failure. (FF_ERR_NULL_POINTER)
201 *
202 **/
203 FF_ERROR FF_DestroyIOMAN(FF_IOMAN *pIoman) {
204
205 // Ensure no NULL pointer was provided.
206 if(!pIoman) {
207 return FF_ERR_NULL_POINTER;
208 }
209
210 // Ensure pPartition pointer was allocated.
211 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_PART)) {
212 FF_FREE(pIoman->pPartition);
213 }
214
215 // Ensure pBlkDevice pointer was allocated.
216 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BLKDEV)) {
217 FF_FREE(pIoman->pBlkDevice);
218 }
219
220 // Ensure pBuffers pointer was allocated.
221 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BUFDESCR)) {
222 FF_FREE(pIoman->pBuffers);
223 }
224
225 // Ensure pCacheMem pointer was allocated.
226 if((pIoman->MemAllocation & FF_IOMAN_ALLOC_BUFFERS)) {
227 FF_FREE(pIoman->pCacheMem);
228 }
229
230 // Destroy any Semaphore that was created.
231 if(pIoman->pSemaphore) {
232 FF_DestroySemaphore(pIoman->pSemaphore);
233 }
234
235 // Finally free the FF_IOMAN object.
236 FF_FREE(pIoman);
237
238 return FF_ERR_NONE;
239 }
240
241 /**
242 * @private
243 * @brief Initialises Buffer Descriptions as part of the FF_IOMAN object initialisation.
244 *
245 * @param pIoman IOMAN Object.
246 *
247 **/
248 static void FF_IOMAN_InitBufferDescriptors(FF_IOMAN *pIoman) {
249 FF_T_UINT16 i;
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));
255 pBuffer++;
256 }
257 }
258
259 /**
260 * @private
261 * @brief Tests the Mode for validity.
262 *
263 * @param Mode Mode of buffer to check.
264 *
265 * @return FF_TRUE when valid, else FF_FALSE.
266 **/
267 /*static FF_T_BOOL FF_IOMAN_ModeValid(FF_T_UINT8 Mode) {
268 if(Mode == FF_MODE_READ || Mode == FF_MODE_WRITE) {
269 return FF_TRUE;
270 }
271 return FF_FALSE;
272 }*/
273
274
275 /**
276 * @private
277 * @brief Fills a buffer with the appropriate sector via the device driver.
278 *
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.
282 *
283 * HT Note: will be called while semaphore claimed (by FF_GetBuffer)
284 *
285 * @return FF_TRUE when valid, else FF_FALSE.
286 **/
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.
290 do{
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);
295 }
296 } while(retVal == FF_ERR_DRIVER_BUSY);
297 if(retVal < 0) {
298 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
299 } else {
300 if(retVal == 1) {
301 return 0; // 1 Block was sucessfully read.
302 } else {
303 return -1; // 0 Blocks we're read, Error!
304 }
305 }
306 }
307 return -1; // error no device diver registered.
308 }
309
310
311 /**
312 * @private
313 * @brief Flushes a buffer to the device driver.
314 *
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.
318 *
319 *
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
322 *
323 * @return FF_TRUE when valid, else FF_FALSE.
324 **/
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.
328 do{
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);
332 }
333 } while(retVal == FF_ERR_DRIVER_BUSY);
334 if(retVal < 0) {
335 return -1; // FF_ERR_DRIVER_FATAL_ERROR was returned Fail!
336 } else {
337 if(retVal == 1) {
338 return FF_ERR_NONE; // 1 Block was sucessfully written.
339 } else {
340 return -1; // 0 Blocks we're written, Error!
341 }
342 }
343 }
344 return -1; // error no device diver registered.
345 }
346
347
348 /**
349 * @private
350 * @brief Flushes all Write cache buffers with no active Handles.
351 *
352 * @param pIoman IOMAN Object.
353 *
354 * @return FF_ERR_NONE on Success.
355 **/
356 FF_ERROR FF_FlushCache(FF_IOMAN *pIoman) {
357
358 FF_T_UINT16 i,x;
359
360 if(!pIoman) {
361 return FF_ERR_NULL_POINTER;
362 }
363
364 FF_PendSemaphore(pIoman->pSemaphore);
365 {
366 for(i = 0; i < pIoman->CacheSize; i++) {
367 if((pIoman->pBuffers + i)->NumHandles == 0 && (pIoman->pBuffers + i)->Modified == FF_TRUE) {
368
369 FF_IOMAN_FlushBuffer(pIoman, (pIoman->pBuffers + i)->Sector, (pIoman->pBuffers + i)->pBuffer);
370
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;
374
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++) {
378 if(x != i) {
379 if((pIoman->pBuffers + x)->Sector == (pIoman->pBuffers + i)->Sector && (pIoman->pBuffers + x)->Mode == FF_MODE_READ) {
380 (pIoman->pBuffers + x)->Modified = FF_TRUE;
381 }
382 }
383 }
384 }
385 }
386 }
387 FF_ReleaseSemaphore(pIoman->pSemaphore);
388
389 return FF_ERR_NONE;
390 }
391
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)) {
394 return FF_TRUE;
395 }
396 return FF_FALSE;
397 }*/
398
399 FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) {
400 FF_BUFFER *pBuffer;
401 FF_BUFFER *pBufLRU = NULL;
402 FF_BUFFER *pBufLHITS = NULL;
403 FF_BUFFER *pBufMatch = NULL;
404
405 FF_T_UINT32 i;
406
407 while(!pBufMatch) {
408 FF_PendSemaphore(pIoman->pSemaphore);
409 {
410 for(i = 0; i < pIoman->CacheSize; i++) {
411 pBuffer = (pIoman->pBuffers + i);
412 if(pBuffer->Sector == Sector && pBuffer->Valid == FF_TRUE) {
413 pBufMatch = pBuffer;
414 } else {
415 if(pBuffer->NumHandles == 0) {
416 pBuffer->LRU += 1;
417
418 if(!pBufLRU) {
419 pBufLRU = pBuffer;
420 }
421 if(!pBufLHITS) {
422 pBufLHITS = pBuffer;
423 }
424
425 if(pBuffer->LRU >= pBufLRU->LRU) {
426 if(pBuffer->LRU == pBufLRU->LRU) {
427 if(pBuffer->Persistance > pBufLRU->Persistance) {
428 pBufLRU = pBuffer;
429 }
430 } else {
431 pBufLRU = pBuffer;
432 }
433 }
434
435 if(pBuffer->Persistance < pBufLHITS->Persistance) {
436 pBufLHITS = pBuffer;
437 }
438 }
439 }
440 }
441
442 if(pBufMatch) {
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);
448 return pBufMatch;
449 }
450
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);
456 return pBufMatch;
457 }
458
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);
465 return pBufMatch;
466 }
467
468 pBufMatch = NULL; // Sector is already in use, keep yielding until its available!
469
470 } else {
471 // Choose a suitable buffer!
472 if(pBufLRU) {
473 // Process the suitable candidate.
474 if(pBufLRU->Modified == FF_TRUE) {
475 FF_IOMAN_FlushBuffer(pIoman, pBufLRU->Sector, pBufLRU->pBuffer);
476 }
477 pBufLRU->Mode = Mode;
478 pBufLRU->Persistance = 1;
479 pBufLRU->LRU = 0;
480 pBufLRU->NumHandles = 1;
481 pBufLRU->Sector = Sector;
482
483 if(Mode == FF_MODE_WRITE) {
484 pBufLRU->Modified = FF_TRUE;
485 } else {
486 pBufLRU->Modified = FF_FALSE;
487 }
488
489 FF_IOMAN_FillBuffer(pIoman, Sector, pBufLRU->pBuffer);
490 pBufLRU->Valid = FF_TRUE;
491 FF_ReleaseSemaphore(pIoman->pSemaphore);
492 return pBufLRU;
493 }
494
495 }
496 }
497 FF_ReleaseSemaphore(pIoman->pSemaphore);
498 FF_Yield(); // Better to go asleep to give low-priority task a chance to release buffer(s)
499 }
500
501 return pBufMatch; // Return the Matched Buffer!
502 }
503
504
505 /**
506 * @private
507 * @brief Releases a buffer resource.
508 *
509 * @param pIoman Pointer to an FF_IOMAN object.
510 * @param pBuffer Pointer to an FF_BUFFER object.
511 *
512 **/
513 void FF_ReleaseBuffer(FF_IOMAN *pIoman, FF_BUFFER *pBuffer) {
514 // Protect description changes with a semaphore.
515 FF_PendSemaphore(pIoman->pSemaphore);
516 {
517 if (pBuffer->NumHandles) {
518 pBuffer->NumHandles--;
519 } else {
520 //printf ("FF_ReleaseBuffer: buffer not claimed\n");
521 }
522 }
523 FF_ReleaseSemaphore(pIoman->pSemaphore);
524 }
525
526 /**
527 * @public
528 * @brief Registers a device driver with FullFAT
529 *
530 * The device drivers must adhere to the specification provided by
531 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
532 *
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.
538 *
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.
540 **/
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;
544 }
545
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
548 }
549
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
552 }
553
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;
558 }
559 if(pIoman->pBlkDevice->fnWriteBlocks) {
560 return FF_ERR_IOMAN_DEV_ALREADY_REGD;
561 }
562 if(pIoman->pBlkDevice->pParam) {
563 return FF_ERR_IOMAN_DEV_ALREADY_REGD;
564 }
565
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;
572
573 return FF_ERR_NONE; // Success
574 }
575
576 /**
577 * @private
578 **/
579 static FF_ERROR FF_DetermineFatType(FF_IOMAN *pIoman) {
580
581 FF_PARTITION *pPart;
582 FF_BUFFER *pBuffer;
583 FF_T_UINT32 testLong;
584 if(pIoman) {
585 pPart = pIoman->pPartition;
586
587 if(pPart->NumClusters < 4085) {
588 // FAT12
589 pPart->Type = FF_T_FAT12;
590 #ifdef FF_FAT_CHECK
591 #ifdef FF_FAT12_SUPPORT
592 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA, FF_MODE_READ);
593 {
594 if(!pBuffer) {
595 return FF_ERR_DEVICE_DRIVER_FAILED;
596 }
597 testLong = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, 0x0000);
598 }
599 FF_ReleaseBuffer(pIoman, pBuffer);
600 if((testLong & 0x3FF) != 0x3F8) {
601 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
602 }
603 #else
604 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
605 #endif
606 #endif
607 #ifdef FF_FAT12_SUPPORT
608 return FF_ERR_NONE;
609 #endif
610
611 } else if(pPart->NumClusters < 65525) {
612 // FAT 16
613 pPart->Type = FF_T_FAT16;
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_T_UINT32) FF_getShort(pBuffer->pBuffer, 0x0000);
621 }
622 FF_ReleaseBuffer(pIoman, pBuffer);
623 if(testLong != 0xFFF8) {
624 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
625 }
626 #endif
627 return FF_ERR_NONE;
628 }
629 else {
630 // FAT 32!
631 pPart->Type = FF_T_FAT32;
632 #ifdef FF_FAT_CHECK
633 pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA, FF_MODE_READ);
634 {
635 if(!pBuffer) {
636 return FF_ERR_DEVICE_DRIVER_FAILED;
637 }
638 testLong = FF_getLong(pBuffer->pBuffer, 0x0000);
639 }
640 FF_ReleaseBuffer(pIoman, pBuffer);
641 if((testLong & 0x0FFFFFF8) != 0x0FFFFFF8) {
642 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
643 }
644 #endif
645 return FF_ERR_NONE;
646 }
647 }
648
649 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
650 }
651
652 static FF_T_SINT8 FF_PartitionCount (FF_T_UINT8 *pBuffer)
653 {
654 FF_T_SINT8 count = 0;
655 FF_T_SINT8 part;
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
664 }
665 return 0; // No MBR and no PBR then no partition found
666 }
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)) {
672 break;
673 }
674 count++;
675 }
676 return count;
677 }
678
679 /**
680 * @public
681 * @brief Mounts the Specified partition, the volume specified by the FF_IOMAN object provided.
682 *
683 * The device drivers must adhere to the specification provided by
684 * FF_WRITE_BLOCKS and FF_READ_BLOCKS.
685 *
686 * @param pIoman FF_IOMAN object.
687 * @param PartitionNumber The primary partition number to be mounted. (0 - 3).
688 *
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)
695 *
696 **/
697 FF_ERROR FF_MountPartition(FF_IOMAN *pIoman, FF_T_UINT8 PartitionNumber) {
698 FF_PARTITION *pPart;
699 FF_BUFFER *pBuffer = 0;
700 int partCount;
701
702 if(!pIoman) {
703 return FF_ERR_NULL_POINTER;
704 }
705
706 if(PartitionNumber > 3) {
707 return FF_ERR_IOMAN_INVALID_PARTITION_NUM;
708 }
709
710 pPart = pIoman->pPartition;
711
712 memset (pIoman->pBuffers, '\0', sizeof(FF_BUFFER) * pIoman->CacheSize);
713 memset (pIoman->pCacheMem, '\0', pIoman->BlkSize * pIoman->CacheSize);
714
715 FF_IOMAN_InitBufferDescriptors(pIoman);
716 pIoman->FirstFile = 0;
717
718 pBuffer = FF_GetBuffer(pIoman, 0, FF_MODE_READ);
719 if(!pBuffer) {
720 return FF_ERR_DEVICE_DRIVER_FAILED;
721 }
722
723 partCount = FF_PartitionCount (pBuffer->pBuffer);
724
725 pPart->BlkSize = FF_getShort(pBuffer->pBuffer, FF_FAT_BYTES_PER_SECTOR);
726
727 if (partCount == 0) { //(pPart->BlkSize % 512) == 0 && pPart->BlkSize > 0) {
728 // Volume is not partitioned (MBR Found)
729 pPart->BeginLBA = 0;
730 } else {
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);
734
735 if(!pPart->BeginLBA) {
736 return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION;
737 }
738 // Now we get the Partition sector.
739 pBuffer = FF_GetBuffer(pIoman, pPart->BeginLBA, FF_MODE_READ);
740 if(!pBuffer) {
741 return FF_ERR_DEVICE_DRIVER_FAILED;
742 }
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;
747 }
748 }
749
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;
753
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);
756
757 pPart->SectorsPerCluster = FF_getChar(pBuffer->pBuffer, FF_FAT_SECTORS_PER_CLUS);
758
759 pPart->BlkFactor = (FF_T_UINT8) (pPart->BlkSize / pIoman->BlkSize); // Set the BlockFactor (How many real-blocks in a fake block!).
760
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);
768 }
769 memcpy (pPart->VolLabel, pBuffer->pBuffer + FF_FAT_32_VOL_LABEL, sizeof pPart->VolLabel);
770 } else { // FAT16
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);
776 }
777 memcpy (pPart->VolLabel, pBuffer->pBuffer + FF_FAT_16_VOL_LABEL, sizeof pPart->VolLabel);
778 }
779
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;
785
786 if(FF_DetermineFatType(pIoman)) {
787 return FF_ERR_IOMAN_NOT_FAT_FORMATTED;
788 }
789
790 #ifdef FF_MOUNT_FIND_FREE
791 pPart->LastFreeCluster = FF_FindFreeCluster(pIoman);
792 pPart->FreeClusterCount = FF_CountFreeClusters(pIoman);
793 #else
794 pPart->LastFreeCluster = 0;
795 pPart->FreeClusterCount = 0;
796 #endif
797
798 return FF_ERR_NONE;
799 }
800
801 /**
802 * @public
803 * @brief Unregister a Blockdevice, so that the IOMAN can be re-used for another device.
804 *
805 * Any active partitions must be Unmounted first.
806 *
807 * @param pIoman FF_IOMAN object.
808 *
809 * @return FF_ERR_NONE on success.
810 **/
811 FF_ERROR FF_UnregisterBlkDevice(FF_IOMAN *pIoman) {
812
813 FF_T_SINT8 RetVal = FF_ERR_NONE;
814
815 if(!pIoman) {
816 return FF_ERR_NULL_POINTER;
817 }
818
819 FF_PendSemaphore(pIoman->pSemaphore);
820 {
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;
826 } else {
827 RetVal = FF_ERR_IOMAN_PARTITION_MOUNTED;
828 }
829 }
830 FF_ReleaseSemaphore(pIoman->pSemaphore);
831
832 return RetVal;
833 }
834
835 /**
836 * @private
837 * @brief Checks the cache for Active Handles
838 *
839 * @param pIoman FF_IOMAN Object.
840 *
841 * @return FF_TRUE if an active handle is found, else FF_FALSE.
842 *
843 * @pre This function must be wrapped with the cache handling semaphore.
844 **/
845 static FF_T_BOOL FF_ActiveHandles(FF_IOMAN *pIoman) {
846 FF_T_UINT32 i;
847 FF_BUFFER *pBuffer;
848
849 for(i = 0; i < pIoman->CacheSize; i++) {
850 pBuffer = (pIoman->pBuffers + i);
851 if(pBuffer->NumHandles) {
852 return FF_TRUE;
853 }
854 }
855
856 return FF_FALSE;
857 }
858
859
860 /**
861 * @public
862 * @brief Unmounts the active partition.
863 *
864 * @param pIoman FF_IOMAN Object.
865 *
866 * @return FF_ERR_NONE on success.
867 **/
868 FF_ERROR FF_UnmountPartition(FF_IOMAN *pIoman) {
869 FF_T_SINT8 RetVal = FF_ERR_NONE;
870
871 if(!pIoman) {
872 return FF_ERR_NULL_POINTER;
873 }
874
875 FF_PendSemaphore(pIoman->pSemaphore); // Ensure that there are no File Handles
876 {
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;
881 } else {
882 RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES;
883 }
884 } else {
885 RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES; // Active handles found on the cache.
886 }
887 }
888 FF_ReleaseSemaphore(pIoman->pSemaphore);
889
890 return RetVal;
891 }
892
893
894 FF_ERROR FF_IncreaseFreeClusters(FF_IOMAN *pIoman, FF_T_UINT32 Count) {
895
896 //FF_PendSemaphore(pIoman->pSemaphore);
897 //{
898 if(!pIoman->pPartition->FreeClusterCount) {
899 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
900 }
901 pIoman->pPartition->FreeClusterCount += Count;
902 //}
903 //FF_ReleaseSemaphore(pIoman->pSemaphore);
904
905 return FF_ERR_NONE;
906 }
907
908 FF_ERROR FF_DecreaseFreeClusters(FF_IOMAN *pIoman, FF_T_UINT32 Count) {
909
910 //FF_lockFAT(pIoman);
911 //{
912 if(!pIoman->pPartition->FreeClusterCount) {
913 pIoman->pPartition->FreeClusterCount = FF_CountFreeClusters(pIoman);
914 }
915 pIoman->pPartition->FreeClusterCount -= Count;
916 //}
917 //FF_unlockFAT(pIoman);
918
919 return FF_ERR_NONE;
920 }
921
922
923 /**
924 * @brief Returns the Block-size of a mounted Partition
925 *
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.
930 *
931 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
932 *
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.
935 **/
936 FF_T_SINT32 FF_GetPartitionBlockSize(FF_IOMAN *pIoman) {
937
938 if(pIoman) {
939 return (FF_T_SINT32) pIoman->pPartition->BlkSize;
940 }
941
942 return FF_ERR_NULL_POINTER;
943 }
944
945 #ifdef FF_64_NUM_SUPPORT
946 /**
947 * @brief Returns the number of bytes contained within the mounted partition or volume.
948 *
949 * @param pIoman FF_IOMAN Object returned from FF_CreateIOMAN()
950 *
951 * @return The total number of bytes that the mounted partition or volume contains.
952 *
953 **/
954 FF_T_UINT64 FF_GetVolumeSize(FF_IOMAN *pIoman) {
955 if(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));
958 }
959 return 0;
960 }
961
962 #else
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));
966 }
967 #endif
968