2 * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
4 * Authors include James Walmsley, Hein Tibosch and Richard Barry
\r
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
7 * this software and associated documentation files (the "Software"), to deal in
\r
8 * the Software without restriction, including without limitation the rights to
\r
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
10 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
11 * subject to the following conditions:
\r
13 * The above copyright notice and this permission notice shall be included in all
\r
14 * copies or substantial portions of the Software.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * https://www.FreeRTOS.org
\r
27 /* Standard includes. */
\r
33 /* LPC18xx includes. */
\r
37 /* FreeRTOS includes. */
\r
38 #include "FreeRTOS.h"
\r
41 #include "portmacro.h"
\r
43 /* FreeRTOS+FAT includes. */
\r
44 #include "ff_sddisk.h"
\r
47 #include "hr_gettime.h"
\r
49 /* Misc definitions. */
\r
50 #define sdSIGNATURE 0x41404342UL
\r
51 #define sdHUNDRED_64_BIT ( 100ull )
\r
52 #define sdBYTES_PER_MB ( 1024ull * 1024ull )
\r
53 #define sdSECTORS_PER_MB ( sdBYTES_PER_MB / 512ull )
\r
54 #define sdIOMAN_MEM_SIZE 4096
\r
55 #define xSDCardInfo ( sd_mmc_cards[ 0 ] )
\r
56 #define sdAligned( pvAddress ) ( ( ( ( size_t ) ( pvAddress ) ) & ( sizeof( size_t ) - 1 ) ) == 0 )
\r
60 /*-----------------------------------------------------------*/
\r
62 /*_RB_ Functions require comment blocks. */
\r
63 static void prvSDMMCSetupWakeup( void *pvInfo );
\r
64 static uint32_t prvSDMMCWait( void );
\r
65 static void prvSDMMCDelay_ms( uint32_t time );
\r
66 static void prvInitialiseCardInfo( void );
\r
67 static int32_t prvSDMMC_Init( void );
\r
68 static int32_t prvFFRead( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk );
\r
69 static int32_t prvFFWrite( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk );
\r
72 /*-----------------------------------------------------------*/
\r
74 /*_RB_ Variables require a comment block where appropriate. */
\r
75 static int32_t lSDDetected = 0;
\r
76 static mci_card_struct xCardInfo;
\r
77 static volatile int32_t lSDIOWaitExit;
\r
78 static BaseType_t xSDCardStatus;
\r
79 static SemaphoreHandle_t xSDCardSemaphore;
\r
80 static SemaphoreHandle_t xPlusFATMutex;
\r
82 /*-----------------------------------------------------------*/
\r
84 #warning Update to make read and write functions static and make use of the FF_Disk_t type.
\r
86 static int32_t prvFFRead( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk )
\r
90 /*_RB_ Many of the comments in this file apply to other functions in the file. */
\r
92 if( ( pxDisk != NULL ) &&
\r
93 ( xSDCardStatus == pdPASS ) &&
\r
94 ( pxDisk->ulSignature == sdSIGNATURE ) &&
\r
95 ( pxDisk->xStatus.bIsInitialised != pdFALSE ) &&
\r
96 ( ulSectorNumber < pxDisk->ulNumberOfSectors ) &&
\r
97 ( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) >= ulSectorCount ) )
\r
100 iReturn = Chip_SDMMC_ReadBlocks( LPC_SDMMC, pucBuffer, ulSectorNumber, ulSectorCount );
\r
102 /*_RB_ I'm guessing 512 is a sector size, but that needs to be clear.
\r
103 Is it defined in a header somewhere? If so we can do a search and
\r
104 replace in files on it as it seems to be used everywhere. */
\r
105 if( iReturn == ( ulSectorCount * 512 ) ) /*_RB_ Signed/unsigned mismatch (twice!) */
\r
107 iReturn = FF_ERR_NONE;
\r
111 /*_RB_ Signed number used to return bitmap (again below). */
\r
112 iReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_READ | FF_ERRFLAG );
\r
117 memset( ( void * ) pucBuffer, '\0', ulSectorCount * 512 );
\r
119 if( pxDisk->xStatus.bIsInitialised != 0 )
\r
121 FF_PRINTF( "prvFFRead: warning: %lu + %lu > %lu\n", ulSectorNumber, ulSectorCount, pxDisk->ulNumberOfSectors );
\r
124 iReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_READ | FF_ERRFLAG );
\r
129 /*-----------------------------------------------------------*/
\r
131 static int32_t prvFFWrite( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk )
\r
135 if( ( pxDisk != NULL ) &&
\r
136 ( xSDCardStatus == pdPASS ) &&
\r
137 ( pxDisk->ulSignature == sdSIGNATURE ) &&
\r
138 ( pxDisk->xStatus.bIsInitialised != pdFALSE ) &&
\r
139 ( ulSectorNumber < pxDisk->ulNumberOfSectors ) &&
\r
140 ( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) >= ulSectorCount ) )
\r
142 iReturn = Chip_SDMMC_WriteBlocks( LPC_SDMMC, pucBuffer, ulSectorNumber, ulSectorCount );
\r
144 if( iReturn == ( ulSectorCount * 512 ) ) /*_RB_ Signed/unsigned mismatch (twice!) */
\r
150 iReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
155 memset( ( void * ) pucBuffer, '\0', ulSectorCount * 512 );
\r
156 if( pxDisk->xStatus.bIsInitialised )
\r
158 FF_PRINTF( "prvFFWrite: warning: %lu + %lu > %lu\n", ulSectorNumber, ulSectorCount, pxDisk->ulNumberOfSectors );
\r
161 iReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
166 /*-----------------------------------------------------------*/
\r
168 void FF_SDDiskFlush( FF_Disk_t *pxDisk )
\r
170 if( ( pxDisk != NULL ) &&
\r
171 ( pxDisk->xStatus.bIsInitialised != pdFALSE ) &&
\r
172 ( pxDisk->pxIOManager != NULL ) )
\r
174 FF_FlushCache( pxDisk->pxIOManager );
\r
177 /*-----------------------------------------------------------*/
\r
179 /* Initialise the SDIO driver and mount an SD card */
\r
180 FF_Disk_t *FF_SDDiskInit( const char *pcName )
\r
182 FF_Error_t xFFError;
\r
183 BaseType_t xPartitionNumber = 0;
\r
184 FF_CreationParameters_t xParameters;
\r
185 FF_Disk_t * pxDisk;
\r
187 xSDCardStatus = prvSDMMC_Init();
\r
188 if( xSDCardStatus == pdPASS )
\r
190 pxDisk = ( FF_Disk_t * ) pvPortMalloc( sizeof( *pxDisk ) );
\r
192 if( pxDisk != NULL )
\r
195 /* Initialise the created disk structure. */
\r
196 memset( pxDisk, '\0', sizeof( *pxDisk ) );
\r
199 if( xPlusFATMutex == NULL)
\r
201 xPlusFATMutex = xSemaphoreCreateRecursiveMutex();
\r
203 pxDisk->ulNumberOfSectors = xCardInfo.card_info.blocknr;
\r
204 pxDisk->ulSignature = sdSIGNATURE;
\r
206 if( xPlusFATMutex != NULL)
\r
208 memset( &xParameters, '\0', sizeof( xParameters ) );
\r
209 xParameters.ulMemorySize = sdIOMAN_MEM_SIZE;
\r
210 xParameters.ulSectorSize = 512;
\r
211 xParameters.fnWriteBlocks = prvFFWrite;
\r
212 xParameters.fnReadBlocks = prvFFRead;
\r
213 xParameters.pxDisk = pxDisk;
\r
215 /* prvFFRead()/prvFFWrite() are not re-entrant and must be
\r
216 protected with the use of a semaphore. */
\r
217 xParameters.xBlockDeviceIsReentrant = pdFALSE;
\r
219 /* The semaphore will be used to protect critical sections in
\r
220 the +FAT driver, and also to avoid concurrent calls to
\r
221 prvFFRead()/prvFFWrite() from different tasks. */
\r
222 xParameters.pvSemaphore = ( void * ) xPlusFATMutex;
\r
224 pxDisk->pxIOManager = FF_CreateIOManger( &xParameters, &xFFError );
\r
226 if( pxDisk->pxIOManager == NULL )
\r
228 FF_PRINTF( "FF_SDDiskInit: FF_CreateIOManger: %s\n", ( const char * ) FF_GetErrMessage( xFFError ) );
\r
229 FF_SDDiskDelete( pxDisk );
\r
234 pxDisk->xStatus.bIsInitialised = pdTRUE;
\r
235 pxDisk->xStatus.bPartitionNumber = xPartitionNumber;
\r
236 if( FF_SDDiskMount( pxDisk ) == 0 )
\r
238 FF_SDDiskDelete( pxDisk );
\r
243 if( pcName == NULL )
\r
247 FF_FS_Add( pcName, pxDisk );
\r
248 FF_PRINTF( "FF_SDDiskInit: Mounted SD-card as root \"%s\"\n", pcName );
\r
249 FF_SDDiskShowPartition( pxDisk );
\r
251 } /* if( pxDisk->pxIOManager != NULL ) */
\r
252 } /* if( xPlusFATMutex != NULL) */
\r
253 } /* if( pxDisk != NULL ) */
\r
256 FF_PRINTF( "FF_SDDiskInit: Malloc failed\n" );
\r
258 } /* if( xSDCardStatus == pdPASS ) */
\r
261 FF_PRINTF( "FF_SDDiskInit: prvSDMMC_Init failed\n" );
\r
267 /*-----------------------------------------------------------*/
\r
269 BaseType_t FF_SDDiskFormat( FF_Disk_t *pxDisk, BaseType_t xPartitionNumber )
\r
272 BaseType_t xReturn = pdFAIL;
\r
274 xError = FF_Unmount( pxDisk );
\r
276 if( FF_isERR( xError ) != pdFALSE )
\r
278 FF_PRINTF( "FF_SDDiskFormat: unmount fails: %08x\n", ( unsigned ) xError );
\r
282 /* Format the drive - try FAT32 with large clusters. */
\r
283 xError = FF_Format( pxDisk, xPartitionNumber, pdFALSE, pdFALSE);
\r
285 if( FF_isERR( xError ) )
\r
287 FF_PRINTF( "FF_SDDiskFormat: %s\n", (const char*)FF_GetErrMessage( xError ) );
\r
291 FF_PRINTF( "FF_SDDiskFormat: OK, now remounting\n" );
\r
292 pxDisk->xStatus.bPartitionNumber = xPartitionNumber;
\r
293 xError = FF_SDDiskMount( pxDisk );
\r
294 FF_PRINTF( "FF_SDDiskFormat: rc %08x\n", ( unsigned )xError );
\r
295 if( FF_isERR( xError ) == pdFALSE )
\r
304 /*-----------------------------------------------------------*/
\r
306 /* Get a pointer to IOMAN, which can be used for all FreeRTOS+FAT functions */
\r
307 BaseType_t FF_SDDiskMount( FF_Disk_t *pxDisk )
\r
309 FF_Error_t xFFError;
\r
310 BaseType_t xReturn;
\r
312 /* Mount the partition */
\r
313 xFFError = FF_Mount( pxDisk, pxDisk->xStatus.bPartitionNumber );
\r
315 if( FF_isERR( xFFError ) )
\r
317 FF_PRINTF( "FF_SDDiskMount: %08lX\n", xFFError );
\r
322 pxDisk->xStatus.bIsMounted = pdTRUE;
\r
323 FF_PRINTF( "****** FreeRTOS+FAT initialized %lu sectors\n", pxDisk->pxIOManager->xPartition.ulTotalSectors );
\r
324 FF_SDDiskShowPartition( pxDisk );
\r
330 /*-----------------------------------------------------------*/
\r
332 FF_IOManager_t *sddisk_ioman( FF_Disk_t *pxDisk )
\r
334 FF_IOManager_t *pxReturn;
\r
336 if( ( pxDisk != NULL ) && ( pxDisk->xStatus.bIsInitialised != pdFALSE ) )
\r
338 pxReturn = pxDisk->pxIOManager;
\r
346 /*-----------------------------------------------------------*/
\r
348 /* Release all resources */
\r
349 BaseType_t FF_SDDiskDelete( FF_Disk_t *pxDisk )
\r
351 if( pxDisk != NULL )
\r
353 pxDisk->ulSignature = 0;
\r
354 pxDisk->xStatus.bIsInitialised = 0;
\r
355 if( pxDisk->pxIOManager != NULL )
\r
357 if( FF_Mounted( pxDisk->pxIOManager ) != pdFALSE )
\r
359 FF_Unmount( pxDisk );
\r
361 FF_DeleteIOManager( pxDisk->pxIOManager );
\r
364 vPortFree( pxDisk );
\r
368 /*-----------------------------------------------------------*/
\r
370 BaseType_t FF_SDDiskShowPartition( FF_Disk_t *pxDisk )
\r
373 uint64_t ullFreeSectors;
\r
374 uint32_t ulTotalSizeMB, ulFreeSizeMB;
\r
375 int iPercentageFree;
\r
376 FF_IOManager_t *pxIOManager;
\r
377 const char *pcTypeName = "unknown type";
\r
378 BaseType_t xReturn = pdPASS;
\r
380 if( pxDisk == NULL )
\r
386 pxIOManager = pxDisk->pxIOManager;
\r
388 FF_PRINTF( "Reading FAT and calculating Free Space\n" );
\r
390 switch( pxIOManager->xPartition.ucType )
\r
393 pcTypeName = "FAT12";
\r
397 pcTypeName = "FAT16";
\r
401 pcTypeName = "FAT32";
\r
405 pcTypeName = "UNKOWN";
\r
409 FF_GetFreeSize( pxIOManager, &xError );
\r
411 ullFreeSectors = pxIOManager->xPartition.ulFreeClusterCount * pxIOManager->xPartition.ulSectorsPerCluster;
\r
412 iPercentageFree = ( int ) ( ( sdHUNDRED_64_BIT * ullFreeSectors + pxIOManager->xPartition.ulDataSectors / 2 ) /
\r
413 ( ( uint64_t )pxIOManager->xPartition.ulDataSectors ) );
\r
415 ulTotalSizeMB = pxIOManager->xPartition.ulDataSectors / sdSECTORS_PER_MB;
\r
416 ulFreeSizeMB = ( uint32_t ) ( ullFreeSectors / sdSECTORS_PER_MB );
\r
418 /* It is better not to use the 64-bit format such as %Lu because it
\r
419 might not be implemented. */
\r
420 FF_PRINTF( "Partition Nr %8u\n", pxDisk->xStatus.bPartitionNumber );
\r
421 FF_PRINTF( "Type %8u (%s)\n", pxIOManager->xPartition.ucType, pcTypeName );
\r
422 FF_PRINTF( "VolLabel '%8s' \n", pxIOManager->xPartition.pcVolumeLabel );
\r
423 FF_PRINTF( "TotalSectors %8lu\n", pxIOManager->xPartition.ulTotalSectors );
\r
424 FF_PRINTF( "SecsPerCluster %8lu\n", pxIOManager->xPartition.ulSectorsPerCluster );
\r
425 FF_PRINTF( "Size %8lu MB\n", ulTotalSizeMB );
\r
426 FF_PRINTF( "FreeSize %8lu MB ( %d perc free )\n", ulFreeSizeMB, iPercentageFree );
\r
431 /*-----------------------------------------------------------*/
\r
433 void SDIO_IRQHandler( void )
\r
435 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
\r
437 /* All SD based register handling is done in the callback function. The SDIO
\r
438 interrupt is not enabled as part of this driver and needs to be
\r
439 enabled/disabled in the callbacks or application as needed. This is to allow
\r
440 flexibility with IRQ handling for applications and RTOSes. */
\r
442 /* Set wait exit flag to tell wait function we are ready. In an RTOS, this
\r
443 would trigger wakeup of a thread waiting for the IRQ. */
\r
444 NVIC_DisableIRQ( SDIO_IRQn );
\r
445 xSemaphoreGiveFromISR( xSDCardSemaphore, &xHigherPriorityTaskWoken );
\r
447 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
\r
449 /*-----------------------------------------------------------*/
\r
451 /* Sets up the SD event driven wakeup */
\r
452 static void prvSDMMCSetupWakeup( void *pvInfo )
\r
454 uint32_t *ulWaitStatus = ( uint32_t * ) pvInfo;
\r
456 /* Wait for IRQ - for an RTOS, you would pend on an event here with a IRQ
\r
458 /*_RB_ Don't understand why this is spinning on a block time of 0. Is it
\r
459 really meant to be != pdFALSE? */
\r
460 while( xSemaphoreTake( xSDCardSemaphore, 0 ) != pdFALSE )
\r
464 NVIC_ClearPendingIRQ( SDIO_IRQn );
\r
466 Chip_SDIF_SetIntMask( LPC_SDMMC, *ulWaitStatus );
\r
467 NVIC_EnableIRQ( SDIO_IRQn );
\r
469 /*-----------------------------------------------------------*/
\r
471 static uint32_t prvSDMMCWait( void )
\r
475 /*_RB_ 2000 needs to be defined and use pdMS_TO_TICKS so the delay period
\r
476 remains constant no matter how the end user sets configTICK_RATE_MS. */
\r
477 xSemaphoreTake( xSDCardSemaphore, 2000 );
\r
479 ulStatus = Chip_SDIF_GetIntStatus( LPC_SDMMC );
\r
480 if( ( ( ulStatus & MCI_INT_CMD_DONE ) == 0 ) || ( lSDIOWaitExit == 0 ) )
\r
482 FF_PRINTF( "Wait SD: int32_t %ld ulStatus 0x%02lX\n", lSDIOWaitExit, ulStatus );
\r
487 /*-----------------------------------------------------------*/
\r
489 static void prvSDMMCDelay_ms( uint32_t ulTime )
\r
491 /* In an RTOS, the thread would sleep allowing other threads to run.
\r
492 For standalone operation, just spin on a timer */
\r
493 vTaskDelay( pdMS_TO_TICKS( ulTime ) );
\r
495 /*-----------------------------------------------------------*/
\r
497 static int32_t prvSDMMC_Init( void )
\r
499 int32_t lSDCardStatus;
\r
501 if( xSDCardSemaphore == NULL )
\r
503 xSDCardSemaphore = xSemaphoreCreateBinary();
\r
504 configASSERT( xSDCardSemaphore );
\r
505 xSemaphoreGive( xSDCardSemaphore );
\r
508 prvInitialiseCardInfo();
\r
509 NVIC_SetPriority( SDIO_IRQn, configSD_INTERRUPT_PRIORITY );
\r
511 /*_RB_ Board_SDMMC_Init() is library specific code that is also specific to
\r
512 the target development board.The SDMMC peripheral should be initialised from
\r
513 the application code before this code is called. */
\r
514 Board_SDMMC_Init();
\r
515 Chip_SDIF_Init( LPC_SDMMC );
\r
516 lSDDetected = !Chip_SDIF_CardNDetect( LPC_SDMMC );
\r
518 Chip_SDIF_PowerOn( LPC_SDMMC );
\r
519 lSDCardStatus = Chip_SDMMC_Acquire( LPC_SDMMC, &xCardInfo );
\r
520 FF_PRINTF( "Acquire: %ld\n", lSDCardStatus );
\r
522 return lSDCardStatus;
\r
524 /*-----------------------------------------------------------*/
\r
526 static void prvInitialiseCardInfo( void )
\r
528 memset( &xCardInfo, 0, sizeof( xCardInfo ) );
\r
529 xCardInfo.card_info.evsetup_cb = prvSDMMCSetupWakeup;
\r
530 xCardInfo.card_info.waitfunc_cb = prvSDMMCWait;
\r
531 xCardInfo.card_info.msdelay_func = prvSDMMCDelay_ms;
\r