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