1 /*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2  *
3  *                 Copyright (c) 2014-2015 Datalight, Inc.
4  *                     All Rights Reserved Worldwide.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; use version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12  *  of 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 along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 /*  Businesses and individuals that for commercial or other reasons cannot
21  *  comply with the terms of the GPLv2 license may obtain a commercial license
22  *  before incorporating Reliance Edge into proprietary software for
23  *  distribution in any form.  Visit http://www.datalight.com/reliance-edge for
24  *  more information.
25  */
26 
27 /** @file
28  *  @brief Implements block device I/O.
29  */
30 #include <FreeRTOS.h>
31 
32 #include <redfs.h>
33 #include <redvolume.h>
34 #include <redosdeviations.h>
35 
36 
37 /*------------------------------------------------------------------------------
38  *   Porting Note:
39  *
40  *   Several example implementations of this module for FreeRTOS are available.
41  *   If you are lucky, you can use one of these implementations; otherwise, these
42  *   can serve as examples of how to implement this service.
43  *  ------------------------------------------------------------------------------*/
44 
45 /** @brief The F_DRIVER example implementation.
46  *
47  *  This implementation is designed to reuse an existing block device driver
48  *  that was written for FreeRTOS+FAT SL.  If you have such a driver, with
49  *  little work it can be "dropped in" and used for Reliance Edge.  The only
50  *  customization required is that gpfnRedOsBDevInit needs to be defined and
51  *  pointed at the F_DRIVERINIT function.  This can be done in this module or in
52  *  another C file.
53  *
54  *  The disadvantage of using the FreeRTOS F_DRIVER functions is that they only
55  *  support single-sector reads and writes.  Reliance Edge will issue
56  *  multi-sector requests, and servicing these one sector at a time will
57  *  significantly slow down the file system.
58  */
59 #define BDEV_F_DRIVER                  ( 0U )
60 
61 /** @brief The FatFs example implementation.
62  *
63  *  This implementation is designed to reuse an existing block device driver
64  *  that was written for FatFs.  If you have such a driver, it can be linked
65  *  in and used immediately.  The FatFs `diskio.h` header must be in the include
66  *  directory path.
67  */
68 #define BDEV_FATFS                     ( 1U )
69 
70 /** @brief The Atmel Studio Framework SD/MMC driver example implementation.
71  *
72  *  This implementation uses a modified version of the open source SD/MMC driver
73  *  included in the Atmel Studio Framework (ASF) and will work as-is for many
74  *  varieties of Atmel hardware.  This example assumes relatively minor
75  *  modifications to the ASF SD/MMC driver to make it support multi-sector read
76  *  and write requests, which greatly improves performance.  The modified driver
77  *  is distributed with Reliance Edge and is included in FreeRTOS Atmel projects
78  *  (such as in projects/freertos/atmel/sam4e-ek/src/ASF).
79  *
80  *  This example can easily be modified to work with an unmodified version of
81  *  the ASF SD/MMC driver.  Simply replace sd_mmc_mem_2_ram_multi() and
82  *  sd_mmc_ram_2_mem_multi() with sd_mmc_mem_2_ram() and sd_mmc_ram_2_mem()
83  *  respectively, and add a for loop to loop over each sector in the request.
84  *  However, as described in the manual, there are considerable performance
85  *  advantages to issuing real multi-sector requests, so using the modified
86  *  driver is recommended.
87  */
88 #define BDEV_ATMEL_SDMMC               ( 2U )
89 
90 /** @brief The ST Microelectronics STM32 SDIO driver example implementation.
91  *
92  *  This implementation accesses the microSD card through the BSP utilities
93  *  provided as part of the STM32Cube package, used with the STM32 HAL drivers.
94  *  The STM3240G-EVAL and STM32F746NG-Discovery boards are currently supported.
95  */
96 #define BDEV_STM32_SDIO                ( 3U )
97 
98 /** @brief The RAM disk example implementation.
99  *
100  *  This implementation uses a RAM disk.  It will allow you to compile and test
101  *  Reliance Edge even if your storage driver is not yet ready.  On typical
102  *  target hardware, the amount of spare RAM will be limited so generally only
103  *  very small disks will be available.
104  */
105 #define BDEV_RAM_DISK                  ( 4U )
106 
107 /** @brief Pick which example implementation is compiled.
108  *
109  *  Must be one of:
110  *  - #BDEV_F_DRIVER
111  *  - #BDEV_FATFS
112  *  - #BDEV_ATMEL_SDMMC
113  *  - #BDEV_STM32_SDIO
114  *  - #BDEV_RAM_DISK
115  */
116 #define BDEV_EXAMPLE_IMPLEMENTATION    BDEV_RAM_DISK
117 
118 
119 static REDSTATUS DiskOpen( uint8_t bVolNum,
120                            BDEVOPENMODE mode );
121 static REDSTATUS DiskClose( uint8_t bVolNum );
122 static REDSTATUS DiskRead( uint8_t bVolNum,
123                            uint64_t ullSectorStart,
124                            uint32_t ulSectorCount,
125                            void * pBuffer );
126 #if REDCONF_READ_ONLY == 0
127     static REDSTATUS DiskWrite( uint8_t bVolNum,
128                                 uint64_t ullSectorStart,
129                                 uint32_t ulSectorCount,
130                                 const void * pBuffer );
131     static REDSTATUS DiskFlush( uint8_t bVolNum );
132 #endif
133 
134 
135 /** @brief Initialize a block device.
136  *
137  *  This function is called when the file system needs access to a block
138  *  device.
139  *
140  *  Upon successful return, the block device should be fully initialized and
141  *  ready to service read/write/flush/close requests.
142  *
143  *  The behavior of calling this function on a block device which is already
144  *  open is undefined.
145  *
146  *  @param bVolNum  The volume number of the volume whose block device is being
147  *                  initialized.
148  *  @param mode     The open mode, indicating the type of access required.
149  *
150  *  @return A negated ::REDSTATUS code indicating the operation result.
151  *
152  *  @retval 0           Operation was successful.
153  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number.
154  *  @retval -RED_EIO    A disk I/O error occurred.
155  */
RedOsBDevOpen(uint8_t bVolNum,BDEVOPENMODE mode)156 REDSTATUS RedOsBDevOpen( uint8_t bVolNum,
157                          BDEVOPENMODE mode )
158 {
159     REDSTATUS ret;
160 
161     if( bVolNum >= REDCONF_VOLUME_COUNT )
162     {
163         ret = -RED_EINVAL;
164     }
165     else
166     {
167         ret = DiskOpen( bVolNum, mode );
168     }
169 
170     return ret;
171 }
172 
173 
174 /** @brief Uninitialize a block device.
175  *
176  *  This function is called when the file system no longer needs access to a
177  *  block device.  If any resource were allocated by RedOsBDevOpen() to service
178  *  block device requests, they should be freed at this time.
179  *
180  *  Upon successful return, the block device must be in such a state that it
181  *  can be opened again.
182  *
183  *  The behavior of calling this function on a block device which is already
184  *  closed is undefined.
185  *
186  *  @param bVolNum  The volume number of the volume whose block device is being
187  *                  uninitialized.
188  *
189  *  @return A negated ::REDSTATUS code indicating the operation result.
190  *
191  *  @retval 0           Operation was successful.
192  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number.
193  */
RedOsBDevClose(uint8_t bVolNum)194 REDSTATUS RedOsBDevClose( uint8_t bVolNum )
195 {
196     REDSTATUS ret;
197 
198     if( bVolNum >= REDCONF_VOLUME_COUNT )
199     {
200         ret = -RED_EINVAL;
201     }
202     else
203     {
204         ret = DiskClose( bVolNum );
205     }
206 
207     return ret;
208 }
209 
210 
211 /** @brief Read sectors from a physical block device.
212  *
213  *  The behavior of calling this function is undefined if the block device is
214  *  closed or if it was opened with ::BDEV_O_WRONLY.
215  *
216  *  @param bVolNum          The volume number of the volume whose block device
217  *                          is being read from.
218  *  @param ullSectorStart   The starting sector number.
219  *  @param ulSectorCount    The number of sectors to read.
220  *  @param pBuffer          The buffer into which to read the sector data.
221  *
222  *  @return A negated ::REDSTATUS code indicating the operation result.
223  *
224  *  @retval 0           Operation was successful.
225  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
226  *                      `NULL`, or @p ullStartSector and/or @p ulSectorCount
227  *                      refer to an invalid range of sectors.
228  *  @retval -RED_EIO    A disk I/O error occurred.
229  */
RedOsBDevRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)230 REDSTATUS RedOsBDevRead( uint8_t bVolNum,
231                          uint64_t ullSectorStart,
232                          uint32_t ulSectorCount,
233                          void * pBuffer )
234 {
235     REDSTATUS ret = 0;
236 
237     if( ( bVolNum >= REDCONF_VOLUME_COUNT ) ||
238         ( ullSectorStart >= gaRedVolConf[ bVolNum ].ullSectorCount ) ||
239         ( ( gaRedVolConf[ bVolNum ].ullSectorCount - ullSectorStart ) < ulSectorCount ) ||
240         ( pBuffer == NULL ) )
241     {
242         ret = -RED_EINVAL;
243     }
244     else
245     {
246         ret = DiskRead( bVolNum, ullSectorStart, ulSectorCount, pBuffer );
247     }
248 
249     return ret;
250 }
251 
252 
253 #if REDCONF_READ_ONLY == 0
254 
255 /** @brief Write sectors to a physical block device.
256  *
257  *  The behavior of calling this function is undefined if the block device is
258  *  closed or if it was opened with ::BDEV_O_RDONLY.
259  *
260  *  @param bVolNum          The volume number of the volume whose block device
261  *                          is being written to.
262  *  @param ullSectorStart   The starting sector number.
263  *  @param ulSectorCount    The number of sectors to write.
264  *  @param pBuffer          The buffer from which to write the sector data.
265  *
266  *  @return A negated ::REDSTATUS code indicating the operation result.
267  *
268  *  @retval 0           Operation was successful.
269  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number, @p pBuffer is
270  *                      `NULL`, or @p ullStartSector and/or @p ulSectorCount
271  *                      refer to an invalid range of sectors.
272  *  @retval -RED_EIO    A disk I/O error occurred.
273  */
RedOsBDevWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)274     REDSTATUS RedOsBDevWrite( uint8_t bVolNum,
275                               uint64_t ullSectorStart,
276                               uint32_t ulSectorCount,
277                               const void * pBuffer )
278     {
279         REDSTATUS ret = 0;
280 
281         if( ( bVolNum >= REDCONF_VOLUME_COUNT ) ||
282             ( ullSectorStart >= gaRedVolConf[ bVolNum ].ullSectorCount ) ||
283             ( ( gaRedVolConf[ bVolNum ].ullSectorCount - ullSectorStart ) < ulSectorCount ) ||
284             ( pBuffer == NULL ) )
285         {
286             ret = -RED_EINVAL;
287         }
288         else
289         {
290             ret = DiskWrite( bVolNum, ullSectorStart, ulSectorCount, pBuffer );
291         }
292 
293         return ret;
294     }
295 
296 
297 /** @brief Flush any caches beneath the file system.
298  *
299  *  This function must synchronously flush all software and hardware caches
300  *  beneath the file system, ensuring that all sectors written previously are
301  *  committed to permanent storage.
302  *
303  *  If the environment has no caching beneath the file system, the
304  *  implementation of this function can do nothing and return success.
305  *
306  *  The behavior of calling this function is undefined if the block device is
307  *  closed or if it was opened with ::BDEV_O_RDONLY.
308  *
309  *  @param bVolNum  The volume number of the volume whose block device is being
310  *                  flushed.
311  *
312  *  @return A negated ::REDSTATUS code indicating the operation result.
313  *
314  *  @retval 0           Operation was successful.
315  *  @retval -RED_EINVAL @p bVolNum is an invalid volume number.
316  *  @retval -RED_EIO    A disk I/O error occurred.
317  */
RedOsBDevFlush(uint8_t bVolNum)318     REDSTATUS RedOsBDevFlush( uint8_t bVolNum )
319     {
320         REDSTATUS ret;
321 
322         if( bVolNum >= REDCONF_VOLUME_COUNT )
323         {
324             ret = -RED_EINVAL;
325         }
326         else
327         {
328             ret = DiskFlush( bVolNum );
329         }
330 
331         return ret;
332     }
333 #endif /* REDCONF_READ_ONLY == 0 */
334 
335 
336 #if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER
337 
338     #include <api_mdriver.h>
339 
340 
341 /*  This must be declared and initialized elsewere (e.g., in project code) to
342  *  point at the initialization function for the F_DRIVER block device.
343  */
344     extern const F_DRIVERINIT gpfnRedOsBDevInit;
345 
346     static F_DRIVER * gapFDriver[ REDCONF_VOLUME_COUNT ];
347 
348 
349 /** @brief Initialize a disk.
350  *
351  *  @param bVolNum  The volume number of the volume whose block device is being
352  *                  initialized.
353  *  @param mode     The open mode, indicating the type of access required.
354  *
355  *  @return A negated ::REDSTATUS code indicating the operation result.
356  *
357  *  @retval 0           Operation was successful.
358  *  @retval -RED_EIO    A disk I/O error occurred.
359  */
DiskOpen(uint8_t bVolNum,BDEVOPENMODE mode)360     static REDSTATUS DiskOpen( uint8_t bVolNum,
361                                BDEVOPENMODE mode )
362     {
363         REDSTATUS ret;
364 
365         ( void ) mode;
366 
367         if( ( gpfnRedOsBDevInit == NULL ) || ( gapFDriver[ bVolNum ] != NULL ) )
368         {
369             ret = -RED_EINVAL;
370         }
371         else
372         {
373             F_DRIVER * pDriver;
374 
375             pDriver = gpfnRedOsBDevInit( bVolNum );
376 
377             if( pDriver != NULL )
378             {
379                 F_PHY geom;
380                 int iErr;
381 
382                 /*  Validate that the geometry is consistent with the volume
383                  *  configuration.
384                  */
385                 iErr = pDriver->getphy( pDriver, &geom );
386 
387                 if( iErr == 0 )
388                 {
389                     if( ( geom.bytes_per_sector != gaRedVolConf[ bVolNum ].ulSectorSize ) ||
390                         ( geom.number_of_sectors < gaRedVolConf[ bVolNum ].ullSectorCount ) )
391                     {
392                         ret = -RED_EINVAL;
393                     }
394                     else
395                     {
396                         gapFDriver[ bVolNum ] = pDriver;
397                         ret = 0;
398                     }
399                 }
400                 else
401                 {
402                     ret = -RED_EIO;
403                 }
404 
405                 if( ret != 0 )
406                 {
407                     pDriver->release( pDriver );
408                 }
409             }
410             else
411             {
412                 ret = -RED_EIO;
413             }
414         }
415 
416         return ret;
417     }
418 
419 
420 /** @brief Uninitialize a disk.
421  *
422  *  @param bVolNum  The volume number of the volume whose block device is being
423  *                  uninitialized.
424  *
425  *  @return A negated ::REDSTATUS code indicating the operation result.
426  *
427  *  @retval 0   Operation was successful.
428  */
DiskClose(uint8_t bVolNum)429     static REDSTATUS DiskClose( uint8_t bVolNum )
430     {
431         REDSTATUS ret;
432 
433         if( gapFDriver[ bVolNum ] == NULL )
434         {
435             ret = -RED_EINVAL;
436         }
437         else
438         {
439             gapFDriver[ bVolNum ]->release( gapFDriver[ bVolNum ] );
440             gapFDriver[ bVolNum ] = NULL;
441 
442             ret = 0;
443         }
444 
445         return ret;
446     }
447 
448 
449 /** @brief Read sectors from a disk.
450  *
451  *  @param bVolNum          The volume number of the volume whose block device
452  *                          is being read from.
453  *  @param ullSectorStart   The starting sector number.
454  *  @param ulSectorCount    The number of sectors to read.
455  *  @param pBuffer          The buffer into which to read the sector data.
456  *
457  *  @return A negated ::REDSTATUS code indicating the operation result.
458  *
459  *  @retval 0           Operation was successful.
460  *  @retval -RED_EIO    A disk I/O error occurred.
461  */
DiskRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)462     static REDSTATUS DiskRead( uint8_t bVolNum,
463                                uint64_t ullSectorStart,
464                                uint32_t ulSectorCount,
465                                void * pBuffer )
466     {
467         REDSTATUS ret = 0;
468         F_DRIVER * pDriver = gapFDriver[ bVolNum ];
469 
470         if( pDriver == NULL )
471         {
472             ret = -RED_EINVAL;
473         }
474         else
475         {
476             uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
477             uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
478             uint32_t ulSectorIdx;
479             int iErr;
480 
481             for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
482             {
483                 iErr = pDriver->readsector( pDriver, &pbBuffer[ ulSectorIdx * ulSectorSize ],
484                                             CAST_ULONG( ullSectorStart + ulSectorIdx ) );
485 
486                 if( iErr != 0 )
487                 {
488                     ret = -RED_EIO;
489                     break;
490                 }
491             }
492         }
493 
494         return ret;
495     }
496 
497 
498     #if REDCONF_READ_ONLY == 0
499 
500 /** @brief Write sectors to a disk.
501  *
502  *  @param bVolNum          The volume number of the volume whose block device
503  *                          is being written to.
504  *  @param ullSectorStart   The starting sector number.
505  *  @param ulSectorCount    The number of sectors to write.
506  *  @param pBuffer          The buffer from which to write the sector data.
507  *
508  *  @return A negated ::REDSTATUS code indicating the operation result.
509  *
510  *  @retval 0           Operation was successful.
511  *  @retval -RED_EINVAL The block device is not open.
512  *  @retval -RED_EIO    A disk I/O error occurred.
513  */
DiskWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)514         static REDSTATUS DiskWrite( uint8_t bVolNum,
515                                     uint64_t ullSectorStart,
516                                     uint32_t ulSectorCount,
517                                     const void * pBuffer )
518         {
519             REDSTATUS ret = 0;
520             F_DRIVER * pDriver = gapFDriver[ bVolNum ];
521 
522             if( pDriver == NULL )
523             {
524                 ret = -RED_EINVAL;
525             }
526             else
527             {
528                 const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
529                 uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
530                 uint32_t ulSectorIdx;
531                 int iErr;
532 
533                 for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
534                 {
535                     /*  We have to cast pbBuffer to non-const since the writesector
536                      *  prototype is flawed, using a non-const pointer for the buffer.
537                      */
538                     iErr = pDriver->writesector( pDriver, CAST_AWAY_CONST( uint8_t, &pbBuffer[ ulSectorIdx * ulSectorSize ] ),
539                                                  CAST_ULONG( ullSectorStart + ulSectorIdx ) );
540 
541                     if( iErr != 0 )
542                     {
543                         ret = -RED_EIO;
544                         break;
545                     }
546                 }
547             }
548 
549             return ret;
550         }
551 
552 
553 /** @brief Flush any caches beneath the file system.
554  *
555  *  @param bVolNum  The volume number of the volume whose block device is being
556  *                  flushed.
557  *
558  *  @return A negated ::REDSTATUS code indicating the operation result.
559  *
560  *  @retval 0   Operation was successful.
561  */
DiskFlush(uint8_t bVolNum)562         static REDSTATUS DiskFlush( uint8_t bVolNum )
563         {
564             REDSTATUS ret;
565 
566             if( gapFDriver[ bVolNum ] == NULL )
567             {
568                 ret = -RED_EINVAL;
569             }
570             else
571             {
572                 /*  The F_DRIVER interface does not include a flush function, so to be
573                  *  reliable the F_DRIVER implementation must use synchronous writes.
574                  */
575                 ret = 0;
576             }
577 
578             return ret;
579         }
580     #endif /* REDCONF_READ_ONLY == 0 */
581 
582 
583 #elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_FATFS
584 
585     #include <task.h>
586     #include <diskio.h>
587 
588 /*  disk_read() and disk_write() use an unsigned 8-bit value to specify the
589  *  sector count, so no transfer can be larger than 255 sectors.
590  */
591     #define MAX_SECTOR_TRANSFER    UINT8_MAX
592 
593 
594 /** @brief Initialize a disk.
595  *
596  *  @param bVolNum  The volume number of the volume whose block device is being
597  *                  initialized.
598  *  @param mode     The open mode, indicating the type of access required.
599  *
600  *  @return A negated ::REDSTATUS code indicating the operation result.
601  *
602  *  @retval 0           Operation was successful.
603  *  @retval -RED_EIO    A disk I/O error occurred.
604  */
DiskOpen(uint8_t bVolNum,BDEVOPENMODE mode)605     static REDSTATUS DiskOpen( uint8_t bVolNum,
606                                BDEVOPENMODE mode )
607     {
608         DSTATUS status;
609         uint32_t ulTries;
610         REDSTATUS ret = 0;
611 
612         /*  With some implementations of disk_initialize(), such as the one
613          *  implemented by Atmel for the ASF, the first time the disk is opened, the
614          *  SD card can take a while to get ready, in which time disk_initialize()
615          *  returns an error.  Try numerous times, waiting half a second after each
616          *  failure.  Empirically, this has been observed to succeed on the second
617          *  try, so trying 10x more than that provides a margin of error.
618          */
619         for( ulTries = 0U; ulTries < 20U; ulTries++ )
620         {
621             /*  Assuming that the volume number is also the correct drive number.
622              *  If this is not the case in your environment, a static constant array
623              *  can be declared to map volume numbers to the correct driver number.
624              */
625             status = disk_initialize( bVolNum );
626 
627             if( status == 0 )
628             {
629                 break;
630             }
631 
632             vTaskDelay( 500U / portTICK_PERIOD_MS );
633         }
634 
635         if( status != 0 )
636         {
637             ret = -RED_EIO;
638         }
639 
640         /*  Retrieve the sector size and sector count to ensure they are compatible
641          *  with our compile-time geometry.
642          */
643         if( ret == 0 )
644         {
645             WORD wSectorSize;
646             DWORD dwSectorCount;
647             DRESULT result;
648 
649             result = disk_ioctl( bVolNum, GET_SECTOR_SIZE, &wSectorSize );
650 
651             if( result == RES_OK )
652             {
653                 result = disk_ioctl( bVolNum, GET_SECTOR_COUNT, &dwSectorCount );
654 
655                 if( result == RES_OK )
656                 {
657                     if( ( wSectorSize != gaRedVolConf[ bVolNum ].ulSectorSize ) ||
658                         ( dwSectorCount < gaRedVolConf[ bVolNum ].ullSectorCount ) )
659                     {
660                         ret = -RED_EINVAL;
661                     }
662                 }
663                 else
664                 {
665                     ret = -RED_EIO;
666                 }
667             }
668             else
669             {
670                 ret = -RED_EIO;
671             }
672         }
673 
674         return ret;
675     }
676 
677 
678 /** @brief Uninitialize a disk.
679  *
680  *  @param bVolNum  The volume number of the volume whose block device is being
681  *                  uninitialized.
682  *
683  *  @return A negated ::REDSTATUS code indicating the operation result.
684  *
685  *  @retval 0   Operation was successful.
686  */
DiskClose(uint8_t bVolNum)687     static REDSTATUS DiskClose( uint8_t bVolNum )
688     {
689         ( void ) bVolNum;
690         return 0;
691     }
692 
693 
694 /** @brief Read sectors from a disk.
695  *
696  *  @param bVolNum          The volume number of the volume whose block device
697  *                          is being read from.
698  *  @param ullSectorStart   The starting sector number.
699  *  @param ulSectorCount    The number of sectors to read.
700  *  @param pBuffer          The buffer into which to read the sector data.
701  *
702  *  @return A negated ::REDSTATUS code indicating the operation result.
703  *
704  *  @retval 0           Operation was successful.
705  *  @retval -RED_EIO    A disk I/O error occurred.
706  */
DiskRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)707     static REDSTATUS DiskRead( uint8_t bVolNum,
708                                uint64_t ullSectorStart,
709                                uint32_t ulSectorCount,
710                                void * pBuffer )
711     {
712         REDSTATUS ret = 0;
713         uint32_t ulSectorIdx = 0U;
714         uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
715         uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
716 
717         while( ulSectorIdx < ulSectorCount )
718         {
719             uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
720             DRESULT result;
721 
722             result = disk_read( bVolNum, &pbBuffer[ ulSectorIdx * ulSectorSize ], ( DWORD ) ( ullSectorStart + ulSectorIdx ), ( BYTE ) ulTransfer );
723 
724             if( result != RES_OK )
725             {
726                 ret = -RED_EIO;
727                 break;
728             }
729 
730             ulSectorIdx += ulTransfer;
731         }
732 
733         return ret;
734     }
735 
736 
737     #if REDCONF_READ_ONLY == 0
738 
739 /** @brief Write sectors to a disk.
740  *
741  *  @param bVolNum          The volume number of the volume whose block device
742  *                          is being written to.
743  *  @param ullSectorStart   The starting sector number.
744  *  @param ulSectorCount    The number of sectors to write.
745  *  @param pBuffer          The buffer from which to write the sector data.
746  *
747  *  @return A negated ::REDSTATUS code indicating the operation result.
748  *
749  *  @retval 0           Operation was successful.
750  *  @retval -RED_EIO    A disk I/O error occurred.
751  */
DiskWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)752         static REDSTATUS DiskWrite( uint8_t bVolNum,
753                                     uint64_t ullSectorStart,
754                                     uint32_t ulSectorCount,
755                                     const void * pBuffer )
756         {
757             REDSTATUS ret = 0;
758             uint32_t ulSectorIdx = 0U;
759             uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
760             const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
761 
762             while( ulSectorIdx < ulSectorCount )
763             {
764                 uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
765                 DRESULT result;
766 
767                 result = disk_write( bVolNum, &pbBuffer[ ulSectorIdx * ulSectorSize ], ( DWORD ) ( ullSectorStart + ulSectorIdx ), ( BYTE ) ulTransfer );
768 
769                 if( result != RES_OK )
770                 {
771                     ret = -RED_EIO;
772                     break;
773                 }
774 
775                 ulSectorIdx += ulTransfer;
776             }
777 
778             return ret;
779         }
780 
781 
782 /** @brief Flush any caches beneath the file system.
783  *
784  *  @param bVolNum  The volume number of the volume whose block device is being
785  *                  flushed.
786  *
787  *  @return A negated ::REDSTATUS code indicating the operation result.
788  *
789  *  @retval 0   Operation was successful.
790  */
DiskFlush(uint8_t bVolNum)791         static REDSTATUS DiskFlush( uint8_t bVolNum )
792         {
793             REDSTATUS ret;
794             DRESULT result;
795 
796             result = disk_ioctl( bVolNum, CTRL_SYNC, NULL );
797 
798             if( result == RES_OK )
799             {
800                 ret = 0;
801             }
802             else
803             {
804                 ret = -RED_EIO;
805             }
806 
807             return ret;
808         }
809     #endif /* REDCONF_READ_ONLY == 0 */
810 
811 
812 #elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_ATMEL_SDMMC
813 
814     #include <task.h>
815 
816     #include <conf_sd_mmc.h>
817     #include <sd_mmc.h>
818     #include <sd_mmc_mem.h>
819     #include <ctrl_access.h>
820 
821 /*  sd_mmc_mem_2_ram_multi() and sd_mmc_ram_2_mem_multi() use an unsigned
822  *  16-bit value to specify the sector count, so no transfer can be larger
823  *  than UINT16_MAX sectors.
824  */
825     #define MAX_SECTOR_TRANSFER    UINT16_MAX
826 
827 
828 /** @brief Initialize a disk.
829  *
830  *  @param bVolNum  The volume number of the volume whose block device is being
831  *                  initialized.
832  *  @param mode     The open mode, indicating the type of access required.
833  *
834  *  @return A negated ::REDSTATUS code indicating the operation result.
835  *
836  *  @retval 0           Operation was successful.
837  *  @retval -RED_EIO    A disk I/O error occurred.
838  *  @retval -RED_EROFS  The device is read-only media and write access was
839  *                      requested.
840  */
DiskOpen(uint8_t bVolNum,BDEVOPENMODE mode)841     static REDSTATUS DiskOpen( uint8_t bVolNum,
842                                BDEVOPENMODE mode )
843     {
844         REDSTATUS ret = 0;
845         uint32_t ulTries;
846         Ctrl_status cs;
847 
848         /*  Note: Assuming the volume number is the same as the SD card slot.  The
849          *  ASF SD/MMC driver supports two SD slots.  This implementation will need
850          *  to be modified if multiple volumes share a single SD card.
851          */
852 
853         /*  The first time the disk is opened, the SD card can take a while to get
854          *  ready, in which time sd_mmc_test_unit_ready() returns either CTRL_BUSY
855          *  or CTRL_NO_PRESENT.  Try numerous times, waiting half a second after
856          *  each failure.  Empirically, this has been observed to succeed on the
857          *  second try, so trying 10x more than that provides a margin of error.
858          */
859         for( ulTries = 0U; ulTries < 20U; ulTries++ )
860         {
861             cs = sd_mmc_test_unit_ready( bVolNum );
862 
863             if( ( cs != CTRL_NO_PRESENT ) && ( cs != CTRL_BUSY ) )
864             {
865                 break;
866             }
867 
868             vTaskDelay( 500U / portTICK_PERIOD_MS );
869         }
870 
871         if( cs == CTRL_GOOD )
872         {
873             #if REDCONF_READ_ONLY == 0
874                 if( mode != BDEV_O_RDONLY )
875                 {
876                     if( sd_mmc_wr_protect( bVolNum ) )
877                     {
878                         ret = -RED_EROFS;
879                     }
880                 }
881 
882                 if( ret == 0 )
883             #endif
884             {
885                 uint32_t ulSectorLast;
886 
887                 IGNORE_ERRORS( sd_mmc_read_capacity( bVolNum, &ulSectorLast ) );
888 
889                 /*  The ASF SD/MMC driver only supports 512-byte sectors.
890                  */
891                 if( ( gaRedVolConf[ bVolNum ].ulSectorSize != 512U ) ||
892                     ( ( ( uint64_t ) ulSectorLast + 1U ) < gaRedVolConf[ bVolNum ].ullSectorCount ) )
893                 {
894                     ret = -RED_EINVAL;
895                 }
896             }
897         }
898         else
899         {
900             ret = -RED_EIO;
901         }
902 
903         return ret;
904     }
905 
906 
907 /** @brief Uninitialize a disk.
908  *
909  *  @param bVolNum  The volume number of the volume whose block device is being
910  *                  uninitialized.
911  *
912  *  @return A negated ::REDSTATUS code indicating the operation result.
913  *
914  *  @retval 0   Operation was successful.
915  */
DiskClose(uint8_t bVolNum)916     static REDSTATUS DiskClose( uint8_t bVolNum )
917     {
918         ( void ) bVolNum;
919         return 0;
920     }
921 
922 
923 /** @brief Read sectors from a disk.
924  *
925  *  @param bVolNum          The volume number of the volume whose block device
926  *                          is being read from.
927  *  @param ullSectorStart   The starting sector number.
928  *  @param ulSectorCount    The number of sectors to read.
929  *  @param pBuffer          The buffer into which to read the sector data.
930  *
931  *  @return A negated ::REDSTATUS code indicating the operation result.
932  *
933  *  @retval 0   Operation was successful.
934  */
DiskRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)935     static REDSTATUS DiskRead( uint8_t bVolNum,
936                                uint64_t ullSectorStart,
937                                uint32_t ulSectorCount,
938                                void * pBuffer )
939     {
940         REDSTATUS ret = 0;
941         uint32_t ulSectorIdx = 0U;
942         uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
943         uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
944 
945         while( ulSectorIdx < ulSectorCount )
946         {
947             uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
948             Ctrl_status cs;
949 
950             cs = sd_mmc_mem_2_ram_multi( bVolNum, ( uint32_t ) ( ullSectorStart + ulSectorIdx ),
951                                          ( uint16_t ) ulTransfer, &pbBuffer[ ulSectorIdx * ulSectorSize ] );
952 
953             if( cs != CTRL_GOOD )
954             {
955                 ret = -RED_EIO;
956                 break;
957             }
958 
959             ulSectorIdx += ulTransfer;
960         }
961 
962         return ret;
963     }
964 
965 
966     #if REDCONF_READ_ONLY == 0
967 
968 /** @brief Write sectors to a disk.
969  *
970  *  @param bVolNum          The volume number of the volume whose block device
971  *                          is being written to.
972  *  @param ullSectorStart   The starting sector number.
973  *  @param ulSectorCount    The number of sectors to write.
974  *  @param pBuffer          The buffer from which to write the sector data.
975  *
976  *  @return A negated ::REDSTATUS code indicating the operation result.
977  *
978  *  @retval 0   Operation was successful.
979  */
DiskWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)980         static REDSTATUS DiskWrite( uint8_t bVolNum,
981                                     uint64_t ullSectorStart,
982                                     uint32_t ulSectorCount,
983                                     const void * pBuffer )
984         {
985             REDSTATUS ret = 0;
986             uint32_t ulSectorIdx = 0U;
987             uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
988             const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
989 
990             while( ulSectorIdx < ulSectorCount )
991             {
992                 uint32_t ulTransfer = REDMIN( ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER );
993                 Ctrl_status cs;
994 
995                 cs = sd_mmc_ram_2_mem_multi( bVolNum, ( uint32_t ) ( ullSectorStart + ulSectorIdx ),
996                                              ( uint16_t ) ulTransfer, &pbBuffer[ ulSectorIdx * ulSectorSize ] );
997 
998                 if( cs != CTRL_GOOD )
999                 {
1000                     ret = -RED_EIO;
1001                     break;
1002                 }
1003 
1004                 ulSectorIdx += ulTransfer;
1005             }
1006 
1007             return ret;
1008         }
1009 
1010 
1011 /** @brief Flush any caches beneath the file system.
1012  *
1013  *  @param bVolNum  The volume number of the volume whose block device is being
1014  *                  flushed.
1015  *
1016  *  @return A negated ::REDSTATUS code indicating the operation result.
1017  *
1018  *  @retval 0   Operation was successful.
1019  */
DiskFlush(uint8_t bVolNum)1020         static REDSTATUS DiskFlush( uint8_t bVolNum )
1021         {
1022             REDSTATUS ret;
1023             Ctrl_status cs;
1024 
1025             /*  The ASF SD/MMC driver appears to write sectors synchronously, so it
1026              *  should be fine to do nothing and return success.  However, Atmel's
1027              *  implementation of the FatFs diskio.c file does the equivalent of the
1028              *  below when the disk is flushed.  Just in case this is important for some
1029              *  non-obvious reason, do the same.
1030              */
1031             cs = sd_mmc_test_unit_ready( bVolNum );
1032 
1033             if( cs == CTRL_GOOD )
1034             {
1035                 ret = 0;
1036             }
1037             else
1038             {
1039                 ret = -RED_EIO;
1040             }
1041 
1042             return ret;
1043         }
1044     #endif /* REDCONF_READ_ONLY == 0 */
1045 
1046 #elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_STM32_SDIO
1047 
1048     #ifdef USE_STM324xG_EVAL
1049         #include <stm324xg_eval.h>
1050         #include <stm324xg_eval_sd.h>
1051     #elif defined( USE_STM32746G_DISCO )
1052         #include <stm32746g_discovery.h>
1053         #include <stm32746g_discovery_sd.h>
1054     #else
1055 
1056 /*  If you are using a compatible STM32 device other than the two listed above
1057  *  and you have SD card driver headers, you can try adding them to the above
1058  *  list.
1059  */
1060         #error "Unsupported device."
1061     #endif
1062 
1063     #if REDCONF_VOLUME_COUNT > 1
1064         #error "The STM32 SDIO block device implementation does not support multiple volumes."
1065     #endif
1066 
1067 
1068     #ifndef USE_HAL_DRIVER
1069         #error "The STM32 StdPeriph driver is not supported. Please use the HAL driver or modify the Reliance Edge block device interface."
1070     #endif
1071 
1072 
1073 /** @brief Number of times to call BSP_SD_GetStatus() before timing out and
1074  *         returning an error.
1075  *
1076  *  See ::CheckStatus().
1077  *
1078  *  NOTE: Datalight has not observed a scenario where BSP_SD_GetStatus()
1079  *  returns SD_TRANSFER_BUSY after a transfer command returns successfully.
1080  *  Set SD_STATUS_TIMEOUT to 0U to skip checking BSP_SD_GetStatus().
1081  */
1082     #define SD_STATUS_TIMEOUT    ( 100000U )
1083 
1084 /** @brief 4-byte aligned buffer to use for DMA transfers when passed in
1085  *         an unaligned buffer.
1086  */
1087     static uint32_t gaulAlignedBuffer[ 512U / sizeof( uint32_t ) ];
1088 
1089 
1090     #if SD_STATUS_TIMEOUT > 0U
1091         static REDSTATUS CheckStatus( void );
1092     #endif
1093 
1094 
1095 /** @brief Initialize a disk.
1096  *
1097  *  @param bVolNum  The volume number of the volume whose block device is being
1098  *                  initialized.
1099  *  @param mode     The open mode, indicating the type of access required.
1100  *
1101  *  @return A negated ::REDSTATUS code indicating the operation result.
1102  *
1103  *  @retval 0           Operation was successful.
1104  *  @retval -RED_EIO    No SD card was found; or BSP_SD_Init() failed.
1105  *  @retval -RED_EINVAL The SD card's block size is not the same as the
1106  *                      configured sector size; or the SD card is not large
1107  *                      enough for the volume; or the volume size is above
1108  *                      4GiB, meaning that part of it cannot be accessed
1109  *                      through the STM32 SDIO driver.
1110  */
DiskOpen(uint8_t bVolNum,BDEVOPENMODE mode)1111     static REDSTATUS DiskOpen( uint8_t bVolNum,
1112                                BDEVOPENMODE mode )
1113     {
1114         REDSTATUS ret = 0;
1115         static bool fSdInitted = false;
1116 
1117         ( void ) mode;
1118 
1119         if( !fSdInitted )
1120         {
1121             if( BSP_SD_Init() == MSD_OK )
1122             {
1123                 fSdInitted = true;
1124             }
1125         }
1126 
1127         if( !fSdInitted )
1128         {
1129             /*  Above initialization attempt failed.
1130              */
1131             ret = -RED_EIO;
1132         }
1133         else if( BSP_SD_IsDetected() == SD_NOT_PRESENT )
1134         {
1135             ret = -RED_EIO;
1136         }
1137         else
1138         {
1139             uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
1140             HAL_SD_CardInfoTypedef sdCardInfo = { { 0 } };
1141 
1142             BSP_SD_GetCardInfo( &sdCardInfo );
1143 
1144             /*  Note: the actual card block size is sdCardInfo.CardBlockSize,
1145              *  but the interface only supports a 512 byte block size. Further,
1146              *  one card has been observed to report a 1024-byte block size,
1147              *  but it worked fine with a 512-byte Reliance Edge ulSectorSize.
1148              */
1149             if( ( ulSectorSize != 512U ) ||
1150                 ( sdCardInfo.CardCapacity < ( gaRedVolConf[ bVolNum ].ullSectorCount * ulSectorSize ) ) )
1151             {
1152                 ret = -RED_EINVAL;
1153             }
1154         }
1155 
1156         return ret;
1157     }
1158 
1159 
1160 /** @brief Uninitialize a disk.
1161  *
1162  *  @param bVolNum  The volume number of the volume whose block device is being
1163  *                  uninitialized.
1164  *
1165  *  @return A negated ::REDSTATUS code indicating the operation result.
1166  *
1167  *  @retval 0   Operation was successful.
1168  */
DiskClose(uint8_t bVolNum)1169     static REDSTATUS DiskClose( uint8_t bVolNum )
1170     {
1171         ( void ) bVolNum;
1172         return 0;
1173     }
1174 
1175 
1176 /** @brief Read sectors from a disk.
1177  *
1178  *  @param bVolNum          The volume number of the volume whose block device
1179  *                          is being read from.
1180  *  @param ullSectorStart   The starting sector number.
1181  *  @param ulSectorCount    The number of sectors to read.
1182  *  @param pBuffer          The buffer into which to read the sector data.
1183  *
1184  *  @return A negated ::REDSTATUS code indicating the operation result.
1185  *
1186  *  @retval 0           Operation was successful.
1187  *  @retval -RED_EIO    A disk I/O error occurred.
1188  */
DiskRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)1189     static REDSTATUS DiskRead( uint8_t bVolNum,
1190                                uint64_t ullSectorStart,
1191                                uint32_t ulSectorCount,
1192                                void * pBuffer )
1193     {
1194         REDSTATUS redStat = 0;
1195         uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
1196         uint8_t bSdError;
1197 
1198         if( IS_UINT32_ALIGNED_PTR( pBuffer ) )
1199         {
1200             bSdError = BSP_SD_ReadBlocks_DMA( CAST_UINT32_PTR( pBuffer ), ullSectorStart * ulSectorSize, ulSectorSize, ulSectorCount );
1201 
1202             if( bSdError != MSD_OK )
1203             {
1204                 redStat = -RED_EIO;
1205             }
1206 
1207             #if SD_STATUS_TIMEOUT > 0U
1208                 else
1209                 {
1210                     redStat = CheckStatus();
1211                 }
1212             #endif
1213         }
1214         else
1215         {
1216             uint32_t ulSectorIdx;
1217 
1218             for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
1219             {
1220                 bSdError = BSP_SD_ReadBlocks_DMA( gaulAlignedBuffer, ( ullSectorStart + ulSectorIdx ) * ulSectorSize, ulSectorSize, 1U );
1221 
1222                 if( bSdError != MSD_OK )
1223                 {
1224                     redStat = -RED_EIO;
1225                 }
1226 
1227                 #if SD_STATUS_TIMEOUT > 0U
1228                     else
1229                     {
1230                         redStat = CheckStatus();
1231                     }
1232                 #endif
1233 
1234                 if( redStat == 0 )
1235                 {
1236                     uint8_t * pbBuffer = CAST_VOID_PTR_TO_UINT8_PTR( pBuffer );
1237 
1238                     RedMemCpy( &pbBuffer[ ulSectorIdx * ulSectorSize ], gaulAlignedBuffer, ulSectorSize );
1239                 }
1240                 else
1241                 {
1242                     break;
1243                 }
1244             }
1245         }
1246 
1247         return redStat;
1248     }
1249 
1250 
1251     #if REDCONF_READ_ONLY == 0
1252 
1253 /** @brief Write sectors to a disk.
1254  *
1255  *  @param bVolNum          The volume number of the volume whose block device
1256  *                          is being written to.
1257  *  @param ullSectorStart   The starting sector number.
1258  *  @param ulSectorCount    The number of sectors to write.
1259  *  @param pBuffer          The buffer from which to write the sector data.
1260  *
1261  *  @return A negated ::REDSTATUS code indicating the operation result.
1262  *
1263  *  @retval 0           Operation was successful.
1264  *  @retval -RED_EIO    A disk I/O error occurred.
1265  */
DiskWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)1266         static REDSTATUS DiskWrite( uint8_t bVolNum,
1267                                     uint64_t ullSectorStart,
1268                                     uint32_t ulSectorCount,
1269                                     const void * pBuffer )
1270         {
1271             REDSTATUS redStat = 0;
1272             uint32_t ulSectorSize = gaRedVolConf[ bVolNum ].ulSectorSize;
1273             uint8_t bSdError;
1274 
1275             if( IS_UINT32_ALIGNED_PTR( pBuffer ) )
1276             {
1277                 bSdError = BSP_SD_WriteBlocks_DMA( CAST_UINT32_PTR( CAST_AWAY_CONST( void, pBuffer ) ), ullSectorStart * ulSectorSize,
1278                                                    ulSectorSize, ulSectorCount );
1279 
1280                 if( bSdError != MSD_OK )
1281                 {
1282                     redStat = -RED_EIO;
1283                 }
1284 
1285                 #if SD_STATUS_TIMEOUT > 0U
1286                     else
1287                     {
1288                         redStat = CheckStatus();
1289                     }
1290                 #endif
1291             }
1292             else
1293             {
1294                 uint32_t ulSectorIdx;
1295 
1296                 for( ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++ )
1297                 {
1298                     const uint8_t * pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR( pBuffer );
1299 
1300                     RedMemCpy( gaulAlignedBuffer, &pbBuffer[ ulSectorIdx * ulSectorSize ], ulSectorSize );
1301 
1302                     bSdError = BSP_SD_WriteBlocks_DMA( gaulAlignedBuffer, ( ullSectorStart + ulSectorIdx ) * ulSectorSize, ulSectorSize, 1U );
1303 
1304                     if( bSdError != MSD_OK )
1305                     {
1306                         redStat = -RED_EIO;
1307                     }
1308 
1309                     #if SD_STATUS_TIMEOUT > 0U
1310                         else
1311                         {
1312                             redStat = CheckStatus();
1313                         }
1314                     #endif
1315 
1316                     if( redStat != 0 )
1317                     {
1318                         break;
1319                     }
1320                 }
1321             }
1322 
1323             return redStat;
1324         }
1325 
1326 
1327 /** @brief Flush any caches beneath the file system.
1328  *
1329  *  @param bVolNum  The volume number of the volume whose block device is being
1330  *                  flushed.
1331  *
1332  *  @return A negated ::REDSTATUS code indicating the operation result.
1333  *
1334  *  @retval 0   Operation was successful.
1335  */
DiskFlush(uint8_t bVolNum)1336         static REDSTATUS DiskFlush( uint8_t bVolNum )
1337         {
1338             /*  Disk transfer is synchronous; nothing to flush.
1339              */
1340             ( void ) bVolNum;
1341             return 0;
1342         }
1343 
1344 
1345         #if SD_STATUS_TIMEOUT > 0U
1346 
1347 /** @brief Wait until BSP_SD_GetStatus returns SD_TRANSFER_OK.
1348  *
1349  *  This function calls BSP_SD_GetStatus repeatedly as long as it returns
1350  *  SD_TRANSFER_BUSY up to SD_STATUS_TIMEOUT times.
1351  *
1352  *  @return A negated ::REDSTATUS code indicating the operation result.
1353  *
1354  *  @retval 0           SD_TRANSFER_OK was returned.
1355  *  @retval -RED_EIO    SD_TRANSFER_ERROR received, or timed out waiting for
1356  *                      SD_TRANSFER_OK.
1357  */
CheckStatus(void)1358             static REDSTATUS CheckStatus( void )
1359             {
1360                 REDSTATUS redStat = 0;
1361                 uint32_t ulTimeout = SD_STATUS_TIMEOUT;
1362                 HAL_SD_TransferStateTypedef transferState;
1363 
1364                 do
1365                 {
1366                     transferState = BSP_SD_GetStatus();
1367                     ulTimeout--;
1368                 } while( ( transferState == SD_TRANSFER_BUSY ) && ( ulTimeout > 0U ) );
1369 
1370                 if( transferState != SD_TRANSFER_OK )
1371                 {
1372                     redStat = -RED_EIO;
1373                 }
1374 
1375                 return redStat;
1376             }
1377         #endif /* if SD_STATUS_TIMEOUT > 0U */
1378 
1379     #endif /* REDCONF_READ_ONLY == 0 */
1380 
1381 #elif BDEV_EXAMPLE_IMPLEMENTATION == BDEV_RAM_DISK
1382 
1383     #include <stdlib.h> /* For ALLOCATE_CLEARED_MEMORY(), which expands to calloc(). */
1384 
1385 
1386     static uint8_t * gapbRamDisk[ REDCONF_VOLUME_COUNT ];
1387 
1388 
1389 /** @brief Initialize a disk.
1390  *
1391  *  @param bVolNum  The volume number of the volume whose block device is being
1392  *                  initialized.
1393  *  @param mode     The open mode, indicating the type of access required.
1394  *
1395  *  @return A negated ::REDSTATUS code indicating the operation result.
1396  *
1397  *  @retval 0           Operation was successful.
1398  *  @retval -RED_EIO    A disk I/O error occurred.
1399  */
DiskOpen(uint8_t bVolNum,BDEVOPENMODE mode)1400     static REDSTATUS DiskOpen( uint8_t bVolNum,
1401                                BDEVOPENMODE mode )
1402     {
1403         REDSTATUS ret = 0;
1404 
1405         ( void ) mode;
1406 
1407         if( gapbRamDisk[ bVolNum ] == NULL )
1408         {
1409             gapbRamDisk[ bVolNum ] = ALLOCATE_CLEARED_MEMORY( gaRedVolume[ bVolNum ].ulBlockCount, REDCONF_BLOCK_SIZE );
1410 
1411             if( gapbRamDisk[ bVolNum ] == NULL )
1412             {
1413                 ret = -RED_EIO;
1414             }
1415         }
1416 
1417         return ret;
1418     }
1419 
1420 
1421 /** @brief Uninitialize a disk.
1422  *
1423  *  @param bVolNum  The volume number of the volume whose block device is being
1424  *                  uninitialized.
1425  *
1426  *  @return A negated ::REDSTATUS code indicating the operation result.
1427  *
1428  *  @retval 0   Operation was successful.
1429  */
DiskClose(uint8_t bVolNum)1430     static REDSTATUS DiskClose( uint8_t bVolNum )
1431     {
1432         REDSTATUS ret;
1433 
1434         if( gapbRamDisk[ bVolNum ] == NULL )
1435         {
1436             ret = -RED_EINVAL;
1437         }
1438         else
1439         {
1440             /*  This implementation uses dynamically allocated memory, but must
1441              *  retain previously written data after the block device is closed, and
1442              *  thus the memory cannot be freed and will remain allocated until
1443              *  reboot.
1444              */
1445             ret = 0;
1446         }
1447 
1448         return ret;
1449     }
1450 
1451 
1452 /** @brief Read sectors from a disk.
1453  *
1454  *  @param bVolNum          The volume number of the volume whose block device
1455  *                          is being read from.
1456  *  @param ullSectorStart   The starting sector number.
1457  *  @param ulSectorCount    The number of sectors to read.
1458  *  @param pBuffer          The buffer into which to read the sector data.
1459  *
1460  *  @return A negated ::REDSTATUS code indicating the operation result.
1461  *
1462  *  @retval 0   Operation was successful.
1463  */
DiskRead(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,void * pBuffer)1464     static REDSTATUS DiskRead( uint8_t bVolNum,
1465                                uint64_t ullSectorStart,
1466                                uint32_t ulSectorCount,
1467                                void * pBuffer )
1468     {
1469         REDSTATUS ret;
1470 
1471         if( gapbRamDisk[ bVolNum ] == NULL )
1472         {
1473             ret = -RED_EINVAL;
1474         }
1475         else
1476         {
1477             uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[ bVolNum ].ulSectorSize;
1478             uint32_t ulByteCount = ulSectorCount * gaRedVolConf[ bVolNum ].ulSectorSize;
1479 
1480             RedMemCpy( pBuffer, &gapbRamDisk[ bVolNum ][ ullByteOffset ], ulByteCount );
1481 
1482             ret = 0;
1483         }
1484 
1485         return ret;
1486     }
1487 
1488 
1489     #if REDCONF_READ_ONLY == 0
1490 
1491 /** @brief Write sectors to a disk.
1492  *
1493  *  @param bVolNum          The volume number of the volume whose block device
1494  *                          is being written to.
1495  *  @param ullSectorStart   The starting sector number.
1496  *  @param ulSectorCount    The number of sectors to write.
1497  *  @param pBuffer          The buffer from which to write the sector data.
1498  *
1499  *  @return A negated ::REDSTATUS code indicating the operation result.
1500  *
1501  *  @retval 0   Operation was successful.
1502  */
DiskWrite(uint8_t bVolNum,uint64_t ullSectorStart,uint32_t ulSectorCount,const void * pBuffer)1503         static REDSTATUS DiskWrite( uint8_t bVolNum,
1504                                     uint64_t ullSectorStart,
1505                                     uint32_t ulSectorCount,
1506                                     const void * pBuffer )
1507         {
1508             REDSTATUS ret;
1509 
1510             if( gapbRamDisk[ bVolNum ] == NULL )
1511             {
1512                 ret = -RED_EINVAL;
1513             }
1514             else
1515             {
1516                 uint64_t ullByteOffset = ullSectorStart * gaRedVolConf[ bVolNum ].ulSectorSize;
1517                 uint32_t ulByteCount = ulSectorCount * gaRedVolConf[ bVolNum ].ulSectorSize;
1518 
1519                 RedMemCpy( &gapbRamDisk[ bVolNum ][ ullByteOffset ], pBuffer, ulByteCount );
1520 
1521                 ret = 0;
1522             }
1523 
1524             return ret;
1525         }
1526 
1527 
1528 /** @brief Flush any caches beneath the file system.
1529  *
1530  *  @param bVolNum  The volume number of the volume whose block device is being
1531  *                  flushed.
1532  *
1533  *  @return A negated ::REDSTATUS code indicating the operation result.
1534  *
1535  *  @retval 0   Operation was successful.
1536  */
DiskFlush(uint8_t bVolNum)1537         static REDSTATUS DiskFlush( uint8_t bVolNum )
1538         {
1539             REDSTATUS ret;
1540 
1541             if( gapbRamDisk[ bVolNum ] == NULL )
1542             {
1543                 ret = -RED_EINVAL;
1544             }
1545             else
1546             {
1547                 ret = 0;
1548             }
1549 
1550             return ret;
1551         }
1552     #endif /* REDCONF_READ_ONLY == 0 */
1553 
1554 #else /* if BDEV_EXAMPLE_IMPLEMENTATION == BDEV_F_DRIVER */
1555 
1556     #error "Invalid BDEV_EXAMPLE_IMPLEMENTATION value"
1557 
1558 #endif /* BDEV_EXAMPLE_IMPLEMENTATION == ... */
1559