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