From 7e223bdaeb6a047b2d87e3eefe7a8864753bae4a Mon Sep 17 00:00:00 2001 From: Simon Hughes Date: Fri, 24 Feb 2017 18:12:16 +0000 Subject: [PATCH] Ported mbed 2 FAT32 filesysetm test cases to mbed 5 tests in basic.cpp. --- config/mbed_app.json | 12 + features/TESTS/filesystem/basic/basic.cpp | 492 +++++++++++++++++- .../TESTS/filesystem/fat_file_system/main.cpp | 151 ++++++ features/TESTS/filesystem/fopen/fopen.cpp | 30 +- features/TESTS/filesystem/spif/main.cpp | 180 +++++++ features/filesystem/sd/SDBlockDevice.cpp | 41 +- features/filesystem/sd/SDBlockDevice.h | 22 +- features/filesystem/spif/README.md | 43 ++ features/filesystem/spif/SPIFBlockDevice.cpp | 348 +++++++++++++ features/filesystem/spif/SPIFBlockDevice.h | 155 ++++++ features/filesystem/test/fsfat_test.c | 2 +- 11 files changed, 1410 insertions(+), 66 deletions(-) create mode 100644 features/TESTS/filesystem/fat_file_system/main.cpp create mode 100644 features/TESTS/filesystem/spif/main.cpp create mode 100644 features/filesystem/spif/README.md create mode 100644 features/filesystem/spif/SPIFBlockDevice.cpp create mode 100644 features/filesystem/spif/SPIFBlockDevice.h diff --git a/config/mbed_app.json b/config/mbed_app.json index c230f26374..b19ac3b6fc 100644 --- a/config/mbed_app.json +++ b/config/mbed_app.json @@ -102,6 +102,18 @@ "SPI_CLK": "SPI_SCK", "SPI_CS": "SPI_CS" }, + "NUMAKER_PFM_M453": { + "SPI_MOSI": "PD_13", + "SPI_MISO": "PD_14", + "SPI_CLK": "PD_15", + "SPI_CS": "PD_12" + }, + "NUMAKER_PFM_NUC472": { + "SPI_MOSI": "PF_0", + "SPI_MISO": "PD_15", + "SPI_CLK": "PD_14", + "SPI_CS": "PD_13" + }, "nRF51822": { "SPI_MOSI": "p12", "SPI_MISO": "p13", diff --git a/features/TESTS/filesystem/basic/basic.cpp b/features/TESTS/filesystem/basic/basic.cpp index 51a5583376..c8187dd135 100644 --- a/features/TESTS/filesystem/basic/basic.cpp +++ b/features/TESTS/filesystem/basic/basic.cpp @@ -43,6 +43,13 @@ * * Consult the documentation under the test-case functions for * a description of the individual test case. + * + * this file includes ports for the mbed 2 test cases from the following locations: + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp. + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp. + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. */ #include "mbed.h" @@ -60,6 +67,7 @@ #include #include #include +#include /* retarget.h is included after errno.h so symbols are mapped to * consistent values for all toolchains */ #include "platform/retarget.h" @@ -80,7 +88,7 @@ using namespace utest::v1; * "DEVICE_SPI": 1, * "MBED_CONF_APP_FSFAT_SDCARD_INSTALLED": 1 * }, - * <<< lines removed >>> + * <<< lines removed >>> */ #if defined(DEVICE_SPI) && defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) @@ -89,13 +97,27 @@ using namespace utest::v1; #define FSFAT_BASIC_TEST_02 fsfat_basic_test_02 #define FSFAT_BASIC_TEST_03 fsfat_basic_test_03 #define FSFAT_BASIC_TEST_04 fsfat_basic_test_04 +#define FSFAT_BASIC_TEST_05 fsfat_basic_test_05 +#define FSFAT_BASIC_TEST_06 fsfat_basic_test_06 +#define FSFAT_BASIC_TEST_07 fsfat_basic_test_07 +#define FSFAT_BASIC_TEST_08 fsfat_basic_test_08 +#define FSFAT_BASIC_TEST_09 fsfat_basic_test_09 +#define FSFAT_BASIC_TEST_10 fsfat_basic_test_10 #define FSFAT_BASIC_MSG_BUF_SIZE 256 +#define FSFAT_BASIC_TEST_05_TEST_STRING "Hello World!" static const char *sd_file_path = "/sd/out.txt"; static const char *sd_mount_pt = "sd"; -const int FSFAT_BASIC_DATA_SIZE = 256; +static const int FSFAT_BASIC_DATA_SIZE = 256; static char fsfat_basic_msg_g[FSFAT_BASIC_MSG_BUF_SIZE]; +static char fsfat_basic_buffer[1024]; +static const int FSFAT_BASIC_KIB_RW = 128; +static Timer fsfat_basic_timer; +static const char *fsfat_basic_bin_filename = "/sd/testfile.bin"; +static const char *fsfat_basic_bin_filename_test_08 = "testfile.bin"; +static const char *fsfat_basic_bin_filename_test_10 = "0:testfile.bin"; + SDBlockDevice sd(MBED_CONF_APP_SPI_MOSI, MBED_CONF_APP_SPI_MISO, MBED_CONF_APP_SPI_CLK, MBED_CONF_APP_SPI_CS); @@ -129,7 +151,7 @@ static control_t fsfat_basic_test_00() // Write these data into the file FSFAT_FENTRYLOG("%s:entered\n", __func__); { - printf("SD: Writing ... "); + FSFAT_DBGLOG("%s:SD: Writing ... ", __func__); FILE *f = fopen(sd_file_path, "w"); if (f) { for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { @@ -139,13 +161,13 @@ static control_t fsfat_basic_test_00() write_result = true; fclose(f); } - printf("[%s]\r\n", write_result ? "OK" : "FAIL"); + FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL"); } TEST_ASSERT_MESSAGE(write_result == true, "Error: write_result is set to false."); // Read back the data from the file and store them in data_read { - printf("SD: Reading data ... "); + FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__); FILE *f = fopen(sd_file_path, "r"); if (f) { read_result = true; @@ -158,7 +180,7 @@ static control_t fsfat_basic_test_00() } fclose(f); } - printf("[%s]\r\n", read_result ? "OK" : "FAIL"); + FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL"); } TEST_ASSERT_MESSAGE(read_result == true, "Error: read_result is set to false."); return CaseNext; @@ -177,8 +199,8 @@ static control_t fsfat_basic_test_01() FSFAT_FENTRYLOG("%s:entered\n", __func__); fp = fopen (sd_file_path, "w+"); - if (fp == NULL) { - printf("errno=%d\n", errno); + if (fp == NULL) { + FSFAT_DBGLOG("errno=%d\n", errno); TEST_ASSERT_MESSAGE(false, "error"); return CaseNext; } @@ -192,7 +214,7 @@ static control_t fsfat_basic_test_01() fp1 = freopen (sd_file_path, "r", fp); TEST_ASSERT_MESSAGE(fp1 == fp, "Error: cannot open file for reading"); - for (i = 1; i <= 255; i++) { + for (i = 1; i <= 255; i++) { ret = fseek (fp, (long) -i, SEEK_END); FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s:Error: fseek() failed (ret=%d).\n", __func__, (int) ret); TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); @@ -282,13 +304,13 @@ static control_t fsfat_basic_test_02() int c = getc(f); if (c == EOF) { - printf("EOF at %u.\n", i); + FSFAT_DBGLOG("EOF at %u.\n", i); lose = 1; break; } else if (c != hello[i]) { - printf("Got '%c' instead of '%c' at %u.\n", + FSFAT_DBGLOG("Got '%c' instead of '%c' at %u.\n", (unsigned char) c, hello[i], i); lose = 1; break; @@ -303,25 +325,25 @@ static control_t fsfat_basic_test_02() register size_t i; for (i = replace_from; i < replace_to; ++i) { if (putc(replace[i], f) == EOF) { - printf("putc('%c') got %s at %u.\n", + FSFAT_DBGLOG("putc('%c') got %s at %u.\n", replace[i], strerror(errno), i); lose = 1; break; } /* WARNING: The problem seems to be that putc() is not writing the 'w' chars into the file - * printf("%s: here1.5. (char = %c, char as int=%d, ret=%d) \n", __func__, replace[i], (int) replace[i], ret); + * FSFAT_DBGLOG("%s: here1.5. (char = %c, char as int=%d, ret=%d) \n", __func__, replace[i], (int) replace[i], ret); */ } } else if (where == -1L) { - printf("ftell got %s (should be at %u).\n", + FSFAT_DBGLOG("ftell got %s (should be at %u).\n", strerror(errno), replace_from); lose = 1; } else { - printf("ftell returns %ld; should be %u.\n", where, replace_from); + FSFAT_DBGLOG("ftell returns %ld; should be %u.\n", where, replace_from); lose = 1; } } @@ -332,12 +354,12 @@ static control_t fsfat_basic_test_02() memset(buf, 0, BUFSIZ); if (fgets(buf, sizeof(buf), f) == NULL) { - printf("fgets got %s.\n", strerror(errno)); + FSFAT_DBGLOG("fgets got %s.\n", strerror(errno)); lose = 1; } else if (strcmp(buf, replace)) { - printf("Read \"%s\" instead of \"%s\".\n", buf, replace); + FSFAT_DBGLOG("Read \"%s\" instead of \"%s\".\n", buf, replace); lose = 1; } } @@ -371,7 +393,7 @@ static control_t fsfat_basic_test_03() static bool fsfat_basic_fileno_check(const char *name, FILE *stream, int fd) { /* ARMCC stdio.h currently does not define fileno() */ -#ifndef TOOLCHAIN_ARM_STD +#ifndef __ARMCC_VERSION int sfd = fileno (stream); FSFAT_DBGLOG("(fileno (%s) = %d) %c= %d\n", name, sfd, sfd == fd ? '=' : '!', fd); @@ -383,7 +405,7 @@ static bool fsfat_basic_fileno_check(const char *name, FILE *stream, int fd) #else /* For ARMCC behave as though test had passed. */ return true; -#endif /* TOOLCHAIN_ARM_STD */ +#endif /* __ARMCC_VERSION */ } /* defines for next test case */ @@ -409,6 +431,8 @@ static bool fsfat_basic_fileno_check(const char *name, FILE *stream, int fd) */ static control_t fsfat_basic_test_04() { + /* ARMCC stdio.h currently does not define fileno() */ +#ifndef __ARMCC_VERSION int ret = -1; ret = fsfat_basic_fileno_check("stdin", stdin, STDIN_FILENO); FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stdin does not have expected file number (expected=%d, fileno=%d.\n", __func__, stdin, fileno(stdin)); @@ -421,7 +445,417 @@ static control_t fsfat_basic_test_04() ret = fsfat_basic_fileno_check("stderr", stderr, STDERR_FILENO); FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stderr does not have expected file number (expected=%d, fileno=%d.\n", __func__, stderr, fileno(stderr)); TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g); - //*/ +#endif /* __ARMCC_VERSION */ + return CaseNext; +} + + +/** @brief basic test to opendir() on a directory. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_05() +{ + FILE *f; + char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + char *buffer = (char *)malloc(sizeof(unsigned char) * strlen(FSFAT_BASIC_TEST_05_TEST_STRING)); + int str_len = strlen(FSFAT_BASIC_TEST_05_TEST_STRING); + int ret = 0; + + FSFAT_DBGLOG("%s:Write files\n", __func__); + char filename[32]; + for (int i = 0; i < 10; i++) { + sprintf(filename, "/sd/test_%d.txt", i); + FSFAT_DBGLOG("Creating file: %s\n", filename); + f = fopen(filename, "w"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + ret = fprintf(f, str); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == strlen(str), fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + + FSFAT_DBGLOG("%s:List files:\n", __func__); + DIR *d = opendir("/sd"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: opendir() failed.\n", __func__); + TEST_ASSERT_MESSAGE(d != NULL, fsfat_basic_msg_g); + + struct dirent *p; + while ((p = readdir(d)) != NULL) + FSFAT_DBGLOG("%s\n", p->d_name); + closedir(d); + + return CaseNext; +} + + +/** @brief basic test to write a file to sd card, and read it back again + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_06() +{ + int ret = -1; + char mac[16]; + mbed_mac_address(mac); + FSFAT_DBGLOG("mac address: %02x,%02x,%02x,%02x,%02x,%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + FILE *f; + char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + char *buffer = (char *)malloc(sizeof(unsigned char) * strlen(FSFAT_BASIC_TEST_05_TEST_STRING)); + int str_len = strlen(FSFAT_BASIC_TEST_05_TEST_STRING); + + f = fopen(sd_file_path, "w"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + ret = fprintf(f, str); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == strlen(str), fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + // Read + f = fopen(sd_file_path, "r"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + int n = fread(fsfat_basic_buffer, sizeof(unsigned char), str_len, f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fread() failed.\n", __func__); + TEST_ASSERT_MESSAGE(n == str_len, fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + return CaseNext; +} + + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_07() +{ + uint8_t data_written[FSFAT_BASIC_DATA_SIZE] = { 0 }; + + // Fill data_written buffer with random data + // Write these data into the file + bool write_result = false; + { + FSFAT_DBGLOG("%s:SD: Writing ... ", __func__); + FILE *f = fopen(sd_file_path, "w"); + if (f) { + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + data_written[i] = rand() % 0XFF; + fprintf(f, "%c", data_written[i]); + } + write_result = true; + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected write failure.\n", __func__); + TEST_ASSERT_MESSAGE(write_result == true, fsfat_basic_msg_g); + } + + // Read back the data from the file and store them in data_read + bool read_result = false; + { + FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__); + FILE *f = fopen(sd_file_path, "r"); + if (f) { + read_result = true; + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + uint8_t data = fgetc(f); + if (data != data_written[i]) { + read_result = false; + break; + } + } + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected read failure.\n", __func__); + TEST_ASSERT_MESSAGE(read_result == true, fsfat_basic_msg_g); + } + return CaseNext; +} + + +static bool fsfat_basic_test_file_write_fhandle(const char *filename, const int kib_rw) +{ + int ret = -1; + FileHandle* file = fs.open(filename, O_WRONLY | O_CREAT | O_TRUNC); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + + int byte_write = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + ret = file->write(fsfat_basic_buffer, sizeof(fsfat_basic_buffer)); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + file->close(); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); + fsfat_basic_timer.reset(); + return true; +} + + +static bool fsfat_basic_test_file_read_fhandle(const char *filename, const int kib_rw) +{ + FileHandle* file = fs.open(filename, O_RDONLY); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + + fsfat_basic_timer.start(); + int byte_read = 0; + while (file->read(fsfat_basic_buffer, sizeof(fsfat_basic_buffer)) == sizeof(fsfat_basic_buffer)) { + byte_read++; + } + fsfat_basic_timer.stop(); + file->close(); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); + fsfat_basic_timer.reset(); + return true; +} + + +static char fsfat_basic_test_random_char() +{ + return rand() % 100; +} + + +/** @brief basic sd card performance test + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_08() +{ + // Test header + FSFAT_DBGLOG("\n%s:SD Card FileHandle Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(0); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_file_write_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_file_read_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } + return CaseNext; +} + + +bool fsfat_basic_test_sf_file_write_stdio(const char *filename, const int kib_rw) +{ + int ret = -1; + FILE* file = fopen(filename, "w"); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + + int byte_write = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + ret = fwrite(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + fclose(file); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); + fsfat_basic_timer.reset(); + return true; +} + + +bool fsfat_basic_test_sf_file_read_stdio(const char *filename, const int kib_rw) +{ + bool result = true; + FILE* file = fopen(filename, "r"); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + fsfat_basic_timer.start(); + int byte_read = 0; + while (fread(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file) == sizeof(fsfat_basic_buffer)) { + byte_read++; + } + fsfat_basic_timer.stop(); + fclose(file); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); + + fsfat_basic_timer.reset(); + return true; +} + + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_09() +{ + // Test header + FSFAT_DBGLOG("\n%s:SD Card Stdio Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(0); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_sf_file_write_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_sf_file_read_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } + return CaseNext; +} + + +bool fsfat_basic_test_file_write_fatfs(const char *filename, const int kib_rw) +{ + FIL file; + FRESULT res = f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + + int byte_write = 0; + unsigned int bytes = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + res = f_write(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + f_close(&file); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); + fsfat_basic_timer.reset(); + return true; +} + +bool fsfat_basic_test_file_read_fatfs(const char *filename, const int kib_rw) +{ + FIL file; + FRESULT res = f_open(&file, filename, FA_READ | FA_OPEN_EXISTING); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + + fsfat_basic_timer.start(); + int byte_read = 0; + unsigned int bytes = 0; + do { + res = f_read(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes); + byte_read++; + } while (res == FR_OK && bytes == sizeof(fsfat_basic_buffer)); + fsfat_basic_timer.stop(); + f_close(&file); + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); + fsfat_basic_timer.reset(); + return true; +} + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_10() +{ + // Test header + FSFAT_DBGLOG("\n%sSD Card FatFS Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename_test_10); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(1); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_file_write_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_file_read_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } return CaseNext; } @@ -432,6 +866,13 @@ static control_t fsfat_basic_test_04() #define FSFAT_BASIC_TEST_02 fsfat_basic_test_dummy #define FSFAT_BASIC_TEST_03 fsfat_basic_test_dummy #define FSFAT_BASIC_TEST_04 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_05 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_06 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_07 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_08 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_09 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_10 fsfat_basic_test_dummy + /** @brief fsfat_basic_test_dummy Dummy test case for testing when platform doesnt have an SDCard installed. * @@ -457,11 +898,16 @@ Case cases[] = { /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ Case("FSFAT_BASIC_TEST_00: fopen()/fgetc()/fprintf()/fclose() test.", FSFAT_BASIC_TEST_00), Case("FSFAT_BASIC_TEST_01: fopen()/fseek()/fclose() test.", FSFAT_BASIC_TEST_01), + /* WARNING: Test case not working but currently not required for PAL support + * Case("FSFAT_BASIC_TEST_02: fopen()/fgets()/fputs()/ftell()/rewind()/remove() test.", FSFAT_BASIC_TEST_02) */ Case("FSFAT_BASIC_TEST_03: tmpnam() test.", FSFAT_BASIC_TEST_03), Case("FSFAT_BASIC_TEST_04: fileno() test.", FSFAT_BASIC_TEST_04), - /* WARNING: Test case not working but currently not required for PAL support - * Case("FSFAT_BASIC_TEST_02: fopen()/fgets()/fputs()/ftell()/rewind()/remove() test.", FSFAT_BASIC_TEST_02) - */ + Case("FSFAT_BASIC_TEST_05: opendir() basic test.", FSFAT_BASIC_TEST_05), + Case("FSFAT_BASIC_TEST_06: fread()/fwrite() file to sdcard.", FSFAT_BASIC_TEST_06), + Case("FSFAT_BASIC_TEST_07: sdcard fwrite() file test.", FSFAT_BASIC_TEST_07), + Case("FSFAT_BASIC_TEST_08: FATFileSystem::read()/write() test.", FSFAT_BASIC_TEST_08), + Case("FSFAT_BASIC_TEST_09: POSIX FILE API fread()/fwrite() test.", FSFAT_BASIC_TEST_09), + Case("FSFAT_BASIC_TEST_10: ChanFS read()/write()) test.", FSFAT_BASIC_TEST_10), }; diff --git a/features/TESTS/filesystem/fat_file_system/main.cpp b/features/TESTS/filesystem/fat_file_system/main.cpp new file mode 100644 index 0000000000..66de43c283 --- /dev/null +++ b/features/TESTS/filesystem/fat_file_system/main.cpp @@ -0,0 +1,151 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "HeapBlockDevice.h" +#include "FATFileSystem.h" +#include +#include "retarget.h" + +using namespace utest::v1; + +// Test block device +#define BLOCK_SIZE 512 +HeapBlockDevice bd(128*BLOCK_SIZE, BLOCK_SIZE); + + +void test_format() { + int err = FATFileSystem::format(&bd); + TEST_ASSERT_EQUAL(0, err); +} + + +// Simple test for reading/writing files +template +void test_read_write() { + FATFileSystem fs("fat"); + + int err = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, err); + + uint8_t *buffer = (uint8_t *)malloc(TEST_SIZE); + TEST_ASSERT(buffer); + + // Fill with random sequence + srand(1); + for (int i = 0; i < TEST_SIZE; i++) { + buffer[i] = 0xff & rand(); + } + + // write and read file + FileHandle *file = fs.open("test_read_write.dat", O_WRONLY | O_CREAT); + TEST_ASSERT(file); + ssize_t size = file->write(buffer, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file->close(); + TEST_ASSERT_EQUAL(0, err); + + file = fs.open("test_read_write.dat", O_RDONLY); + TEST_ASSERT(file); + size = file->read(buffer, TEST_SIZE); + TEST_ASSERT_EQUAL(TEST_SIZE, size); + err = file->close(); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(1); + for (int i = 0; i < TEST_SIZE; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), buffer[i]); + } + + err = fs.unmount(); + TEST_ASSERT_EQUAL(0, err); +} + +// Simple test for iterating dir entries +void test_read_dir() { + FATFileSystem fs("fat"); + + int err = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, err); + + err = fs.mkdir("test_read_dir", S_IRWXU | S_IRWXG | S_IRWXO); + TEST_ASSERT_EQUAL(0, err); + + err = fs.mkdir("test_read_dir/test_dir", S_IRWXU | S_IRWXG | S_IRWXO); + TEST_ASSERT_EQUAL(0, err); + + FileHandle *file = fs.open("test_read_dir/test_file", O_WRONLY | O_CREAT); + TEST_ASSERT(file); + err = file->close(); + TEST_ASSERT_EQUAL(0, err); + + // Iterate over dir checking for known files + DirHandle *dir = fs.opendir("test_read_dir"); + TEST_ASSERT(dir); + + struct dirent *de; + bool test_dir_found = false; + bool test_file_found = true; + + while ((de = readdir(dir))) { + printf("d_name: %.32s, d_type: %x\n", de->d_name, de->d_type); + + if (strcmp(de->d_name, "test_dir") == 0) { + test_dir_found = true; + TEST_ASSERT_EQUAL(DT_DIR, de->d_type); + } else if (strcmp(de->d_name, "test_file") == 0) { + test_file_found = true; + TEST_ASSERT_EQUAL(DT_REG, de->d_type); + } else { + char *buf = new char[NAME_MAX]; + snprintf(buf, NAME_MAX, "Unexpected file \"%s\"", de->d_name); + TEST_ASSERT_MESSAGE(false, buf); + } + } + + TEST_ASSERT_MESSAGE(test_dir_found, "Could not find \"test_dir\""); + TEST_ASSERT_MESSAGE(test_file_found, "Could not find \"test_file\""); + + err = dir->closedir(); + TEST_ASSERT_EQUAL(0, err); + + err = fs.unmount(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(10, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing formating", test_format), + Case("Testing read write < block", test_read_write), + Case("Testing read write > block", test_read_write<2*BLOCK_SIZE>), + Case("Testing dir iteration", test_read_dir), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/features/TESTS/filesystem/fopen/fopen.cpp b/features/TESTS/filesystem/fopen/fopen.cpp index 81efcbee6a..e35259d93a 100644 --- a/features/TESTS/filesystem/fopen/fopen.cpp +++ b/features/TESTS/filesystem/fopen/fopen.cpp @@ -40,12 +40,15 @@ * consistent values for all toolchains */ #include "platform/retarget.h" -/* This is needed for stat() test, but is not available on ARMCC */ -#ifdef TOOLCHAIN_GCC +/* This is needed for stat() test, but is not available on ARMCC. + * The following checks whether GCC_ARM compiler is being used because: + * - both the ARMCC compiler and the GCC_ARM compile define __GNUC__. + * - only the ARMCC compiler defines __ARMCC_VERSION. + * - hence if __ARMCC_VERSION is not defined and __GNUC__ is defined, it must be GCC_ARM. */ +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) #include #endif using namespace utest::v1; -//using namespace mbed; /// @cond FSFAT_DOXYGEN_DISABLE #ifdef FSFAT_DEBUG @@ -507,7 +510,6 @@ control_t fsfat_fopen_test_04(const size_t call_count) } - /// @cond FSFAT_DOXYGEN_DISABLE typedef struct fsfat_fopen_kv_name_ascii_node { uint32_t code; @@ -768,7 +770,7 @@ control_t fsfat_fopen_test_07(const size_t call_count) TEST_ASSERT_MESSAGE(f == NULL, fsfat_fopen_utest_msg_g); /* check errno is set correctly */ -#ifdef TOOLCHAIN_GCC +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) /* Store errno so the current value set is not changed by new function call */ errno_val = errno; FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno has unexpected value (errno != 0 expected) (filename=%s, errno=%d).\n", __func__, filename, errno); @@ -779,7 +781,7 @@ control_t fsfat_fopen_test_07(const size_t call_count) ret = ferror(f); FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() did not return non-zero value when error exists (filename=%s, ret=%d).\n", __func__, filename, (int) ret); TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g); -#endif /* TOOLCHAIN_GCC */ +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ return CaseNext; } @@ -844,7 +846,7 @@ control_t fsfat_fopen_test_08(const size_t call_count) /* the fwrite() should fail and return 0. */ TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); -#ifdef TOOLCHAIN_GCC +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) /* check that errno is set. ARMCC appears not to set errno for fwrite() failure. */ FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected zero value for errno (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno); TEST_ASSERT_MESSAGE(errno != 0, fsfat_fopen_utest_msg_g); @@ -852,7 +854,7 @@ control_t fsfat_fopen_test_08(const size_t call_count) /* check that errno is set to the expected value (this may change differ for different libc's) */ FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno != EBADF (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno); TEST_ASSERT_MESSAGE(errno == EBADF, fsfat_fopen_utest_msg_g); -#endif /* TOOLCHAIN_GCC */ +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ /* check clearerr() return clears the error */ clearerr(fp); @@ -1073,7 +1075,7 @@ control_t fsfat_fopen_test_12(const size_t call_count) FSFAT_FENTRYLOG("%s:entered\n", __func__); (void) call_count; -#ifdef TOOLCHAIN_GCC +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) /* start from a known state i.e. directory to be created in not present */ while(node->filename != NULL) { @@ -1118,7 +1120,7 @@ control_t fsfat_fopen_test_12(const size_t call_count) fsfat_filepath_remove_all((char*) node->filename); node++; } -#endif /* TOOLCHAIN_GCC */ +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ return CaseNext; } @@ -1187,7 +1189,9 @@ static fsfat_kv_data_t fsfat_fopen_test_14_kv_data[] = { */ control_t fsfat_fopen_test_14(const size_t call_count) { - char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1]; +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + + char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1]; char *pos = NULL; int32_t ret = -1; size_t len = 0; @@ -1197,8 +1201,6 @@ control_t fsfat_fopen_test_14(const size_t call_count) FSFAT_FENTRYLOG("%s:entered\n", __func__); (void) call_count; -#ifdef TOOLCHAIN_GCC - TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); /* start from a known state i.e. directory to be created in not present */ @@ -1246,7 +1248,7 @@ control_t fsfat_fopen_test_14(const size_t call_count) /* clean up after successful test */ fsfat_filepath_remove_all((char*) node->filename); -#endif /* TOOLCHAIN_GCC */ +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ return CaseNext; } diff --git a/features/TESTS/filesystem/spif/main.cpp b/features/TESTS/filesystem/spif/main.cpp new file mode 100644 index 0000000000..46c4656590 --- /dev/null +++ b/features/TESTS/filesystem/spif/main.cpp @@ -0,0 +1,180 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "SPIFBlockDevice.h" +#include + +using namespace utest::v1; + +#ifndef SPIF_INSTALLED +// todo: sdh +//#define SPIF_INSTALLED defined(TARGET_K82F) +#define SPIF_INSTALLED defined(TARGET_K64F) +#endif + +#if !SPIF_INSTALLED +#error [NOT_SUPPORTED] SPIF Required +#endif + +/* +#if defined(TARGET_K82F) +#define TEST_PINS PTE2, PTE4, PTE1, PTE5 +#define TEST_FREQ 40000000 +#else +#define TEST_PINS D11, D12, D13, D10 +#define TEST_FREQ 1000000 +#endif +*/ +#if defined(TARGET_K64F) +#define TEST_PINS PTD2, PTD3, PTD1, PTD0 +//#define TEST_FREQ 40000000 +//#define TEST_FREQ 1000000 +#define TEST_FREQ 50000 +#else +#error "no TEST_PINS defined" +#endif + + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 + +/* +const struct { + const char *name; + bd_size_t (BlockDevice::*method)() const; +} ATTRS[] = { + {"read size", &BlockDevice::get_read_size}, + {"program size", &BlockDevice::get_program_size}, + {"erase size", &BlockDevice::get_erase_size}, + {"total size", &BlockDevice::size}, +}; +*/ +const struct { + const char *name; + bd_size_t (BlockDevice::*method)() const; +} ATTRS[] = { + {"read size", &BlockDevice::get_read_size}, + {"program size", &BlockDevice::get_program_size}, + {"erase size", &BlockDevice::get_erase_size}, +// {"total size", &BlockDevice::size}, +}; + +// todo: fix me +void test_read_write() { + SPIFBlockDevice bd(TEST_PINS, TEST_FREQ); + + int err = bd.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i = 3; i >= 0; i--) { + bd_size_t size = (bd.*ATTRS[a].method)(); + if (size >= (1ULL << 10*i)) { + printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[a].name, size >> 10*i, prefixes[i], size); + break; + } + } + } + + + bd_size_t block_size = bd.get_erase_size(); + uint8_t *write_block = new uint8_t[block_size]; + uint8_t *read_block = new uint8_t[block_size]; + uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK]; + +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + unsigned addrwidth = ceil(log(bd.size()-1) / log(16))+1; +#else + unsigned addrwidth = 0; +#endif + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + // Find a random block + bd_addr_t block = (rand()*block_size) % bd.size(); + + // Use next random number as temporary seed to keep + // the address progressing in the pseudorandom sequence + unsigned seed = rand(); + + // Fill with random sequence + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + printf("test %0*llx:%llu...\n", addrwidth, block, block_size); + + err = bd.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("write %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", write_block[i]); + } + printf("...\n"); + + err = bd.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("read %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", read_block[i]); + } + printf("...\n"); + + + //for (int i = 0; i < block_size; i++) { + // printf("%02x:%02x:%s\n", write_block[i], read_block[i], write_block[i] == read_block[i] ? "1" : "0"); + //} + + // Find error mask for debugging + memset(error_mask, 0, TEST_ERROR_MASK); + bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8); + + srand(seed); + for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) { + for (bd_size_t j = 0; j < error_scale; j++) { + if ((0xff & rand()) != read_block[i*error_scale + j]) { + error_mask[i/8] |= 1 << (i%8); + } + } + } + + printf("error %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", error_mask[i]); + } + printf("\n"); + + // Check that the data was unmodified + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + } + + err = bd.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(30, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing read write random blocks", test_read_write), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/features/filesystem/sd/SDBlockDevice.cpp b/features/filesystem/sd/SDBlockDevice.cpp index 9490ef10ed..a027c3008c 100644 --- a/features/filesystem/sd/SDBlockDevice.cpp +++ b/features/filesystem/sd/SDBlockDevice.cpp @@ -123,6 +123,13 @@ #define SD_DBG 0 +#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ +#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ +#define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ +#define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ +#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ +#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ + SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs) : _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0) { @@ -158,7 +165,7 @@ SDBlockDevice::~SDBlockDevice() #define SDCARD_V2 2 #define SDCARD_V2HC 3 -bd_error_t SDBlockDevice::_initialise_card() +int SDBlockDevice::_initialise_card() { _dbg = SD_DBG; // Set to SCK for initialisation, and clock card with cs = 1 @@ -173,7 +180,7 @@ bd_error_t SDBlockDevice::_initialise_card() // send CMD0, should return with all zeros except IDLE STATE set (bit 0) if (_cmd(0, 0) != R1_IDLE_STATE) { debug_if(_dbg, "No disk, or could not put SD card in to SPI idle state\n"); - return BD_ERROR_NO_DEVICE; + return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; } // send CMD8 to determine whther it is ver 2.x @@ -188,7 +195,7 @@ bd_error_t SDBlockDevice::_initialise_card() } } -bd_error_t SDBlockDevice::_initialise_card_v1() +int SDBlockDevice::_initialise_card_v1() { for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { _cmd(55, 0); @@ -203,7 +210,7 @@ bd_error_t SDBlockDevice::_initialise_card_v1() return BD_ERROR_DEVICE_ERROR; } -bd_error_t SDBlockDevice::_initialise_card_v2() +int SDBlockDevice::_initialise_card_v2() { for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { wait_ms(50); @@ -221,10 +228,10 @@ bd_error_t SDBlockDevice::_initialise_card_v2() return BD_ERROR_DEVICE_ERROR; } -bd_error_t SDBlockDevice::init() +int SDBlockDevice::init() { _lock.lock(); - bd_error_t err = _initialise_card(); + int err = _initialise_card(); _is_initialized = (err == BD_ERROR_OK); if (!_is_initialized) { debug_if(_dbg, "Fail to initialize card\n"); @@ -247,21 +254,21 @@ bd_error_t SDBlockDevice::init() return BD_ERROR_OK; } -bd_error_t SDBlockDevice::deinit() +int SDBlockDevice::deinit() { return 0; } -bd_error_t SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) +int SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) { if (!is_valid_program(addr, size)) { - return BD_ERROR_PARAMETER; + return SD_BLOCK_DEVICE_ERROR_PARAMETER; } _lock.lock(); if (!_is_initialized) { _lock.unlock(); - return BD_ERROR_NO_INIT; + return SD_BLOCK_DEVICE_ERROR_NO_INIT; } const uint8_t *buffer = static_cast(b); @@ -283,16 +290,16 @@ bd_error_t SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) return 0; } -bd_error_t SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) +int SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) { if (!is_valid_read(addr, size)) { - return BD_ERROR_PARAMETER; + return SD_BLOCK_DEVICE_ERROR_PARAMETER; } _lock.lock(); if (!_is_initialized) { _lock.unlock(); - return BD_ERROR_PARAMETER; + return SD_BLOCK_DEVICE_ERROR_PARAMETER; } uint8_t *buffer = static_cast(b); @@ -314,22 +321,22 @@ bd_error_t SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) return 0; } -bd_error_t SDBlockDevice::erase(bd_addr_t addr, bd_size_t size) +int SDBlockDevice::erase(bd_addr_t addr, bd_size_t size) { return 0; } -bd_size_t SDBlockDevice::get_read_size() +bd_size_t SDBlockDevice::get_read_size() const { return 512; } -bd_size_t SDBlockDevice::get_program_size() +bd_size_t SDBlockDevice::get_program_size() const { return 512; } -bd_size_t SDBlockDevice::get_erase_size() +bd_size_t SDBlockDevice::get_erase_size() const { return 512; } diff --git a/features/filesystem/sd/SDBlockDevice.h b/features/filesystem/sd/SDBlockDevice.h index 1c7d2a87d6..51afd64036 100644 --- a/features/filesystem/sd/SDBlockDevice.h +++ b/features/filesystem/sd/SDBlockDevice.h @@ -57,13 +57,13 @@ public: * * @return 0 on success or a negative error code on failure */ - virtual bd_error_t init(); + virtual int init(); /** Deinitialize a block device * * @return 0 on success or a negative error code on failure */ - virtual bd_error_t deinit(); + virtual int deinit(); /** Read blocks from a block device * @@ -72,7 +72,7 @@ public: * @param size Size to read in bytes, must be a multiple of read block size * @return 0 on success, negative error code on failure */ - virtual bd_error_t read(void *buffer, bd_addr_t addr, bd_size_t size); + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); /** Program blocks to a block device * @@ -83,7 +83,7 @@ public: * @param size Size to write in bytes, must be a multiple of program block size * @return 0 on success, negative error code on failure */ - virtual bd_error_t program(const void *buffer, bd_addr_t addr, bd_size_t size); + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); /** Erase blocks on a block device * @@ -93,27 +93,27 @@ public: * @param size Size to erase in bytes, must be a multiple of erase block size * @return 0 on success, negative error code on failure */ - virtual bd_error_t erase(bd_addr_t addr, bd_size_t size); + virtual int erase(bd_addr_t addr, bd_size_t size); /** Get the size of a readable block * * @return Size of a readable block in bytes */ - virtual bd_size_t get_read_size(); + virtual bd_size_t get_read_size() const; /** Get the size of a programable block * * @return Size of a programable block in bytes * @note Must be a multiple of the read size */ - virtual bd_size_t get_program_size(); + virtual bd_size_t get_program_size() const; /** Get the size of a eraseable block * * @return Size of a eraseable block in bytes * @note Must be a multiple of the program size */ - virtual bd_size_t get_erase_size(); + virtual bd_size_t get_erase_size() const; /** Get the total size of the underlying device * @@ -132,9 +132,9 @@ private: int _cmdx(int cmd, int arg); int _cmd8(); int _cmd58(); - bd_error_t _initialise_card(); - bd_error_t _initialise_card_v1(); - bd_error_t _initialise_card_v2(); + int _initialise_card(); + int _initialise_card_v1(); + int _initialise_card_v2(); int _read(uint8_t * buffer, uint32_t length); int _write(const uint8_t *buffer, uint32_t length); diff --git a/features/filesystem/spif/README.md b/features/filesystem/spif/README.md new file mode 100644 index 0000000000..310e463832 --- /dev/null +++ b/features/filesystem/spif/README.md @@ -0,0 +1,43 @@ +# SPI Flash Driver + +Block device driver for NOR based SPI flash devices that support SFDP. + +NOR based SPI flash supports byte-sized read and writes, with an erase size of around 4kbytes. An erase sets a block to all 1s, with successive writes clearing set bits. + +More info on NOR flash can be found on wikipedia: +https://en.wikipedia.org/wiki/Flash_memory#NOR_memories + +``` cpp +// Here's an example using the MX25R SPI flash device on the K82F +#include "mbed.h" +#include "SPIFBlockDevice.h" + +// Create flash device on SPI bus with PTE5 as chip select +SPIFBlockDevice spif(PTE2, PTE4, PTE1, PTE5); + +int main() +{ + printf("spif test\n"); + + // Initialize the SPI flash device and print the memory layout + spif.init(); + printf("spif size: %llu\n", spif.size()); + printf("spif read size: %llu\n", spif.get_read_size()); + printf("spif program size: %llu\n", spif.get_program_size()); + printf("spif erase size: %llu\n", spif.get_erase_size()); + + // Write "Hello World!" to the first block + uint8_t *buffer = malloc(spif.get_erase_size()); + sprintf(buffer, "Hello World!\n"); + spif.erase(0, spif.get_erase_size()); + spif.program(buffer, 0, spif.get_erase_size()); + + // Read back what was stored + spif.read(buffer, 0, spif.get_erase_size()); + printf("%s", buffer); + + // Deinitialize the device + spif.deinit(); +} +``` + diff --git a/features/filesystem/spif/SPIFBlockDevice.cpp b/features/filesystem/spif/SPIFBlockDevice.cpp new file mode 100644 index 0000000000..ec7da8f8c5 --- /dev/null +++ b/features/filesystem/spif/SPIFBlockDevice.cpp @@ -0,0 +1,348 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SPIFBlockDevice.h" + + +// Read/write/erase sizes +#define SPIF_READ_SIZE 1 +#define SPIF_PROG_SIZE 1 +#define SPIF_SE_SIZE 4096 +#define SPIF_TIMEOUT 10000 + +// Debug available +#define SPIF_DEBUG 0 + +#define SPIF_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ +#define SPIF_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ +#define SPIF_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ +#define SPIF_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ +#define SPIF_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ +#define SPIF_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ + +// MX25R Series Register Command Table. +enum ops { + SPIF_NOP = 0x00, // No operation + SPIF_READ = 0x03, // Read data + SPIF_PROG = 0x02, // Program data + SPIF_SE = 0x20, // 4KB Sector Erase + SPIF_CE = 0xc7, // Chip Erase + SPIF_SFDP = 0x5a, // Read SFDP + SPIF_WREN = 0x06, // Write Enable + SPIF_WRDI = 0x04, // Write Disable + SPIF_RDSR = 0x05, // Read Status Register + SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID +}; + +// Status register from RDSR +// [- stuff -| wel | wip ] +// [- 6 -| 1 | 1 ] +#define SPIF_WEL 0x2 +#define SPIF_WIP 0x1 + + +SPIFBlockDevice::SPIFBlockDevice( + PinName mosi, PinName miso, PinName sclk, PinName cs, int freq) + : _spi(mosi, miso, sclk), _cs(cs), _size(0) +{ + _cs = 1; + _spi.frequency(freq); +} + +int SPIFBlockDevice::init() +{ + // Check for vendor specific hacks, these should move into more general + // handling when possible. RDID is not used to verify a device is attached. + uint8_t id[3]; + _cmdread(SPIF_RDID, 0, 3, 0x0, id); + + switch (id[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue gbpu instruction to clear + _wren(); + _cmdwrite(0x98, 0, 0, 0x0, NULL); + break; + } + + // Check that device is doing ok + int err = _sync(); + if (err) { + return SPIF_BLOCK_DEVICE_ERROR_NO_DEVICE; + } + + // Check JEDEC serial flash discoverable parameters for device + // specific info + uint8_t header[16]; + _cmdread(SPIF_SFDP, 4, 16, 0x0, header); + + // Verify SFDP signature for sanity + // Also check that major/minor version is acceptable + if (!(memcmp(&header[0], "SFDP", 4) == 0 && header[5] == 1)) { + return BD_ERROR_DEVICE_ERROR; + } + + // The SFDP spec indicates the standard table is always at offset 0 + // in the parameter headers, we check just to be safe + if (!(header[8] == 0 && header[10] == 1)) { + return BD_ERROR_DEVICE_ERROR; + } + + // Parameter table pointer, spi commands are BE, SFDP is LE, + // also sfdp command expects extra read wait byte + uint32_t table_addr = ( + (header[14] << 24) | + (header[13] << 16) | + (header[12] << 8 )); + uint8_t table[8]; + _cmdread(SPIF_SFDP, 4, 8, table_addr, table); + + // Check erase size, currently only supports 4kbytes + // TODO support erase size != 4kbytes? + // TODO support other erase opcodes from the sector descriptions + if ((table[0] & 0x3) != 0x1 || table[1] != SPIF_SE) { + return BD_ERROR_DEVICE_ERROR; + } + + // Check address size, currently only supports 3byte addresses + // TODO support address > 3bytes? + // TODO check for devices larger than 2Gbits? + if ((table[2] & 0x4) != 0 || (table[7] & 0x80) != 0) { + return BD_ERROR_DEVICE_ERROR; + } + + // Get device density, stored as size in bits - 1 + uint32_t density = ( + (table[7] << 24) | + (table[6] << 16) | + (table[5] << 8 ) | + (table[4] << 0 )); + _size = (density/8) + 1; + + return 0; +} + +int SPIFBlockDevice::deinit() +{ + // Latch write disable just to keep noise + // from changing the device + _cmdwrite(SPIF_WRDI, 0, 0, 0x0, NULL); + + return 0; +} + +void SPIFBlockDevice::_cmdread( + uint8_t op, uint32_t addrc, uint32_t retc, + uint32_t addr, uint8_t *rets) +{ + _cs = 0; + _spi.write(op); + + for (uint32_t i = 0; i < addrc; i++) { + _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + } + + for (uint32_t i = 0; i < retc; i++) { + rets[i] = _spi.write(0); + } + _cs = 1; + + if (SPIF_DEBUG) { + printf("spif <- %02x", op); + for (uint32_t i = 0; i < addrc; i++) { + if (i < addrc) { + printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); + } else { + printf(" "); + } + } + printf(" "); + for (uint32_t i = 0; i < 16 && i < retc; i++) { + printf("%02x", rets[i]); + } + if (retc > 16) { + printf("..."); + } + printf("\n"); + } +} + +void SPIFBlockDevice::_cmdwrite( + uint8_t op, uint32_t addrc, uint32_t argc, + uint32_t addr, const uint8_t *args) +{ + _cs = 0; + _spi.write(op); + + for (uint32_t i = 0; i < addrc; i++) { + _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + } + + for (uint32_t i = 0; i < argc; i++) { + _spi.write(args[i]); + } + _cs = 1; + + if (SPIF_DEBUG) { + printf("spif -> %02x", op); + for (uint32_t i = 0; i < addrc; i++) { + if (i < addrc) { + printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); + } else { + printf(" "); + } + } + printf(" "); + for (uint32_t i = 0; i < 16 && i < argc; i++) { + printf("%02x", args[i]); + } + if (argc > 16) { + printf("..."); + } + printf("\n"); + } +} + +int SPIFBlockDevice::_sync() +{ + for (int i = 0; i < SPIF_TIMEOUT; i++) { + // Read status register until write not-in-progress + uint8_t status; + _cmdread(SPIF_RDSR, 0, 1, 0x0, &status); + + // Check WIP bit + if (!(status & SPIF_WIP)) { + return 0; + } + + wait_ms(1); + } + + return BD_ERROR_DEVICE_ERROR; +} + +int SPIFBlockDevice::_wren() +{ + _cmdwrite(SPIF_WREN, 0, 0, 0x0, NULL); + + for (int i = 0; i < SPIF_TIMEOUT; i++) { + // Read status register until write latch is enabled + uint8_t status; + _cmdread(SPIF_RDSR, 0, 1, 0x0, &status); + + // Check WEL bit + if (status & SPIF_WEL) { + return 0; + } + + wait_ms(1); + } + + return BD_ERROR_DEVICE_ERROR; +} + +int SPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_read(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + _cmdread(SPIF_READ, 3, size, addr, static_cast(buffer)); + return 0; +} + +int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_program(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + while (size > 0) { + int err = _wren(); + if (err) { + return err; + } + + // Write up to 256 bytes a page + // TODO handle unaligned programs + uint32_t off = addr % 256; + uint32_t chunk = (off + size < 256) ? size : (256-off); + _cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast(buffer)); + buffer = static_cast(buffer) + chunk; + addr += chunk; + size -= chunk; + + wait_ms(1); + + err = _sync(); + if (err) { + return err; + } + } + + return 0; +} + +int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_erase(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + while (size > 0) { + int err = _wren(); + if (err) { + return err; + } + + // Erase 4kbyte sectors + // TODO support other erase sizes? + uint32_t chunk = 4096; + _cmdwrite(SPIF_SE, 3, 0, addr, NULL); + addr += chunk; + size -= chunk; + + err = _sync(); + if (err) { + return err; + } + } + + return 0; +} + +bd_size_t SPIFBlockDevice::get_read_size() const +{ + return SPIF_READ_SIZE; +} + +bd_size_t SPIFBlockDevice::get_program_size() const +{ + return SPIF_PROG_SIZE; +} + +bd_size_t SPIFBlockDevice::get_erase_size() const +{ + return SPIF_SE_SIZE; +} + +bd_size_t SPIFBlockDevice::size() +{ + return _size; +} diff --git a/features/filesystem/spif/SPIFBlockDevice.h b/features/filesystem/spif/SPIFBlockDevice.h new file mode 100644 index 0000000000..9b20294b35 --- /dev/null +++ b/features/filesystem/spif/SPIFBlockDevice.h @@ -0,0 +1,155 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SPIF_BLOCK_DEVICE_H +#define MBED_SPIF_BLOCK_DEVICE_H + +/* If the target has no SPI support then SPIF is not supported */ +#ifdef DEVICE_SPI + +#include +#include "BlockDevice.h" + + +/** BlockDevice for SPI based flash devices + * such as the MX25R or SST26F016B + * + * @code + * #include "mbed.h" + * #include "SPIFBlockDevice.h" + * + * // Create mx25r on SPI bus with PTE5 as chip select + * SPIFBlockDevice mx25r(PTE2, PTE4, PTE1, PTE5); + * + * int main() { + * printf("mx25r test\n"); + * mx52r.init(); + * printf("mx25r size: %llu\n", mx25r.size()); + * printf("mx25r read size: %llu\n", mx25r.get_read_size()); + * printf("mx25r program size: %llu\n", mx25r.get_program_size()); + * printf("mx25r erase size: %llu\n", mx25r.get_erase_size()); + * + * uint8_t *buffer = malloc(mx25r.get_erase_size()); + * sprintf(buffer, "Hello World!\n"); + * mx25r.erase(0, mx25r.get_erase_size()); + * mx25r.program(buffer, 0, mx25r.get_erase_size()); + * mx25r.read(buffer, 0, mx25r.get_erase_size()); + * printf("%s", buffer); + * + * mx25r.deinit(); + * } + */ +class SPIFBlockDevice : public BlockDevice +{ +public: + /** Creates a SPIFBlockDevice on a SPI bus specified by pins + * + * @param mosi SPI master out, slave in pin + * @param miso SPI master in, slave out pin + * @param sclk SPI clock pin + * @param csel SPI chip select pin + * @param freq Clock speed of the SPI bus (defaults to 40MHz) + */ + SPIFBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, int freq=4000000); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size(); + +private: + // Master side hardware + SPI _spi; + DigitalOut _cs; + + // Device configuration discovered through sfdp + bd_size_t _size; + + // Internal functions + int _wren(); + int _sync(); + void _cmdread(uint8_t op, uint32_t addrc, uint32_t retc, + uint32_t addr, uint8_t *rets); + void _cmdwrite(uint8_t op, uint32_t addrc, uint32_t argc, + uint32_t addr, const uint8_t *args); +}; + + +#endif /* DEVICE_SPI */ + +#endif /* MBED_SPIF_BLOCK_DEVICE_H */ diff --git a/features/filesystem/test/fsfat_test.c b/features/filesystem/test/fsfat_test.c index 9cfad02f25..98d13d9919 100644 --- a/features/filesystem/test/fsfat_test.c +++ b/features/filesystem/test/fsfat_test.c @@ -31,7 +31,7 @@ #ifdef FSFAT_DEBUG uint32_t fsfat_optDebug_g = 1; -uint32_t fsfat_optLogLevel_g = FSFAT_LOG_NONE; /*FSFAT_LOG_NONE|FSFAT_LOG_ERR|FSFAT_LOG_DEBUG|FSFAT_LOG_FENTRY */ +uint32_t fsfat_optLogLevel_g = FSFAT_LOG_NONE; /*FSFAT_LOG_NONE|FSFAT_LOG_ERR|FSFAT_LOG_DEBUG|FSFAT_LOG_FENTRY; */ #endif /* ruler for measuring text strings */