mirror of https://github.com/ARMmbed/mbed-os.git
Ported mbed 2 FAT32 filesysetm test cases to mbed 5 tests in basic.cpp.
parent
b939ebca60
commit
7e223bdaeb
|
@ -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",
|
||||
|
|
|
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <algorithm>
|
||||
/* 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),
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 <stdlib.h>
|
||||
#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 <ssize_t TEST_SIZE>
|
||||
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<BLOCK_SIZE/2>),
|
||||
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);
|
||||
}
|
|
@ -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 <sys/stat.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
#include "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
|
||||
#include "SPIFBlockDevice.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
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);
|
||||
}
|
|
@ -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<const uint8_t*>(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<uint8_t *>(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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
```
|
||||
|
|
@ -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<uint8_t *>(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<const uint8_t *>(buffer));
|
||||
buffer = static_cast<const uint8_t*>(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;
|
||||
}
|
|
@ -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 <mbed.h>
|
||||
#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 */
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue