From 6218078d8fdfdd0f9c4c11e0f630eda01164e152 Mon Sep 17 00:00:00 2001 From: CYFS <2805686936@qq.com> Date: Wed, 26 Aug 2026 15:28:18 +0800 Subject: [PATCH] [UTEST][SDIO] Add block device read and write test --- components/drivers/sdio/utest/Kconfig | 50 +++ components/drivers/sdio/utest/SConscript | 15 + .../drivers/sdio/utest/sdio_read_write_tc.c | 372 ++++++++++++++++++ components/utilities/utest/Kconfig | 1 + 4 files changed, 438 insertions(+) create mode 100644 components/drivers/sdio/utest/Kconfig create mode 100644 components/drivers/sdio/utest/SConscript create mode 100644 components/drivers/sdio/utest/sdio_read_write_tc.c diff --git a/components/drivers/sdio/utest/Kconfig b/components/drivers/sdio/utest/Kconfig new file mode 100644 index 00000000000..cbb86bd85a2 --- /dev/null +++ b/components/drivers/sdio/utest/Kconfig @@ -0,0 +1,50 @@ +menu "SDIO Test" + +config RT_UTEST_SDIO + bool "SDIO Block Read and Write Test" + default n + depends on RT_USING_SDIO + help + Verify SDIO block-device read and write paths with a real SD card. + The test writes directly to the configured sectors and does not use + DFS or format the card. + +if RT_UTEST_SDIO + +config RT_SDIO_UTEST_DEVICE_NAME + string "SDIO block device name" + default "sd0" + help + Block device used by the test, for example sd0 or sd0p0. + +config RT_SDIO_UTEST_START_SECTOR + hex "SDIO test start sector" + default 0x0 + help + First sector used by the test. Existing data in the selected sectors + will be overwritten. + +config RT_SDIO_UTEST_SECTOR_COUNT + int "SDIO test sector count" + range 1 128 + default 8 + help + Number of contiguous sectors used by each transfer. + +config RT_SDIO_UTEST_LOOP_COUNT + int "SDIO test loop count" + range 1 100 + default 3 + help + Number of write/read verification loops at each test location. + +config RT_SDIO_UTEST_DEVICE_WAIT_MS + int "SDIO device wait time" + range 0 60000 + default 3000 + help + Time to wait for the SDIO block device to appear before failing. + +endif + +endmenu diff --git a/components/drivers/sdio/utest/SConscript b/components/drivers/sdio/utest/SConscript new file mode 100644 index 00000000000..f8ec8db8912 --- /dev/null +++ b/components/drivers/sdio/utest/SConscript @@ -0,0 +1,15 @@ +Import('rtconfig') +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd, cwd + '/../../include'] + +if GetDepend('RT_UTEST_SDIO'): + src += Glob('*.c') + +group = DefineGroup('utestcases', src, + depend = ['RT_USING_UTESTCASES', 'RT_USING_SDIO'], + CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/sdio/utest/sdio_read_write_tc.c b/components/drivers/sdio/utest/sdio_read_write_tc.c new file mode 100644 index 00000000000..221dfd61ab3 --- /dev/null +++ b/components/drivers/sdio/utest/sdio_read_write_tc.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-26 Codex add SDIO block read and write utest + */ + +/** + * Test Case Name: SDIO Block Read and Write Test + * + * Test Objectives: + * - Verify SDIO block-device geometry and read/write operations. + * - Verify single-block and multi-block transfers at the beginning and end + * of the configured block device. + * - Verify data remains readable after closing and reopening the device. + * + * Test Environment: + * - Insert a writable SD card. + * - Do not mount or otherwise access the same card while this test runs. + * - Existing data in the configured sectors will be overwritten. + */ + +#include +#include +#include +#include "utest.h" + +#ifdef RT_UTEST_SDIO + +#define SDIO_UTEST_DEVICE_NAME RT_SDIO_UTEST_DEVICE_NAME +#define SDIO_UTEST_START_SECTOR ((rt_off_t)RT_SDIO_UTEST_START_SECTOR) +#define SDIO_UTEST_SECTOR_COUNT ((rt_size_t)RT_SDIO_UTEST_SECTOR_COUNT) +#define SDIO_UTEST_LOOP_COUNT ((rt_size_t)RT_SDIO_UTEST_LOOP_COUNT) +#define SDIO_UTEST_DEVICE_WAIT_MS RT_SDIO_UTEST_DEVICE_WAIT_MS + +#define SDIO_UTEST_SECTOR_SIZE 512u + +static rt_device_t sdio_test_dev; +static struct rt_device_blk_geometry sdio_test_geome; +static rt_uint8_t *sdio_test_write_buf; +static rt_uint8_t *sdio_test_read_buf; +static rt_size_t sdio_test_buf_size; +static rt_bool_t sdio_test_opened; + +static void sdio_test_free_buffers(void) +{ + if (sdio_test_write_buf != RT_NULL) + { + rt_free(sdio_test_write_buf); + sdio_test_write_buf = RT_NULL; + } + if (sdio_test_read_buf != RT_NULL) + { + rt_free(sdio_test_read_buf); + sdio_test_read_buf = RT_NULL; + } + + sdio_test_buf_size = 0; +} + +static rt_device_t sdio_test_find_device(void) +{ + rt_device_t dev; + rt_tick_t wait_ticks; + rt_tick_t start; + + wait_ticks = rt_tick_from_millisecond(SDIO_UTEST_DEVICE_WAIT_MS); + start = rt_tick_get(); + + do + { + dev = rt_device_find(SDIO_UTEST_DEVICE_NAME); + if (dev != RT_NULL) + { + return dev; + } + + if (wait_ticks == 0) + { + break; + } + + rt_thread_mdelay(100); + } while ((rt_tick_t)(rt_tick_get() - start) < wait_ticks); + + return RT_NULL; +} + +static void sdio_test_fill_pattern(rt_uint8_t *buffer, rt_uint8_t seed) +{ + rt_size_t i; + + for (i = 0; i < sdio_test_buf_size; i++) + { + buffer[i] = (rt_uint8_t)(seed ^ (rt_uint8_t)(i * 17u + (i >> 3))); + } +} + +static rt_err_t sdio_test_transfer(rt_off_t sector, + rt_size_t sector_count, + rt_uint8_t seed) +{ + rt_ssize_t result; + rt_size_t transfer_size; + + transfer_size = sdio_test_geome.bytes_per_sector * sector_count; + + sdio_test_fill_pattern(sdio_test_write_buf, seed); + rt_memset(sdio_test_read_buf, 0, sdio_test_buf_size); + + result = rt_device_write(sdio_test_dev, + sector, + sdio_test_write_buf, + sector_count); + if (result != (rt_ssize_t)sector_count) + { + LOG_E("SDIO write failed at sector %ld, result: %ld", + (long)sector, (long)result); + return -RT_EIO; + } + + result = rt_device_read(sdio_test_dev, + sector, + sdio_test_read_buf, + sector_count); + if (result != (rt_ssize_t)sector_count) + { + LOG_E("SDIO read failed at sector %ld, result: %ld", + (long)sector, (long)result); + return -RT_EIO; + } + + if (rt_memcmp(sdio_test_write_buf, + sdio_test_read_buf, + transfer_size) != 0) + { + LOG_E("SDIO data verification failed at sector %ld", (long)sector); + return -RT_EIO; + } + + return RT_EOK; +} + +static void sdio_test_check_geometry(void) +{ + uassert_not_null(sdio_test_dev); + uassert_int_equal(sdio_test_dev->type, RT_Device_Class_Block); + uassert_int_equal(sdio_test_geome.bytes_per_sector, + SDIO_UTEST_SECTOR_SIZE); + uassert_value_greater(sdio_test_geome.sector_count, 0); +} + +static void sdio_test_start_sector(void) +{ + rt_size_t i; + rt_err_t result; + + for (i = 0; i < SDIO_UTEST_LOOP_COUNT; i++) + { + result = sdio_test_transfer(SDIO_UTEST_START_SECTOR, + SDIO_UTEST_SECTOR_COUNT, + (rt_uint8_t)(0x21u + i)); + uassert_int_equal(result, RT_EOK); + if (result != RT_EOK) + { + return; + } + } +} + +static void sdio_test_end_sector(void) +{ + rt_off_t end_sector; + rt_size_t i; + rt_err_t result; + + end_sector = (rt_off_t)(sdio_test_geome.sector_count - + SDIO_UTEST_SECTOR_COUNT); + if (end_sector == SDIO_UTEST_START_SECTOR) + { + LOG_I("SDIO test range covers the whole device; skip duplicate end test"); + return; + } + + for (i = 0; i < SDIO_UTEST_LOOP_COUNT; i++) + { + result = sdio_test_transfer(end_sector, + SDIO_UTEST_SECTOR_COUNT, + (rt_uint8_t)(0x61u + i)); + uassert_int_equal(result, RT_EOK); + if (result != RT_EOK) + { + return; + } + } +} + +static void sdio_test_single_sector(void) +{ + rt_err_t result; + + result = sdio_test_transfer(SDIO_UTEST_START_SECTOR, 1, 0x11); + uassert_int_equal(result, RT_EOK); +} + +static void sdio_test_reopen(void) +{ + rt_ssize_t result; + rt_err_t err; + rt_uint8_t seed = 0xA5; + + sdio_test_fill_pattern(sdio_test_write_buf, seed); + result = rt_device_write(sdio_test_dev, + SDIO_UTEST_START_SECTOR, + sdio_test_write_buf, + SDIO_UTEST_SECTOR_COUNT); + uassert_int_equal(result, (rt_ssize_t)SDIO_UTEST_SECTOR_COUNT); + if (result != (rt_ssize_t)SDIO_UTEST_SECTOR_COUNT) + { + return; + } + + err = rt_device_close(sdio_test_dev); + uassert_int_equal(err, RT_EOK); + if (err != RT_EOK) + { + return; + } + sdio_test_opened = RT_FALSE; + + err = rt_device_open(sdio_test_dev, RT_DEVICE_OFLAG_RDWR); + uassert_int_equal(err, RT_EOK); + if (err != RT_EOK) + { + return; + } + sdio_test_opened = RT_TRUE; + + rt_memset(sdio_test_read_buf, 0, sdio_test_buf_size); + result = rt_device_read(sdio_test_dev, + SDIO_UTEST_START_SECTOR, + sdio_test_read_buf, + SDIO_UTEST_SECTOR_COUNT); + uassert_int_equal(result, (rt_ssize_t)SDIO_UTEST_SECTOR_COUNT); + if (result != (rt_ssize_t)SDIO_UTEST_SECTOR_COUNT) + { + return; + } + + uassert_buf_equal(sdio_test_write_buf, + sdio_test_read_buf, + sdio_test_buf_size); +} + +static rt_err_t utest_tc_init(void) +{ + rt_err_t result; + + sdio_test_dev = sdio_test_find_device(); + if (sdio_test_dev == RT_NULL) + { + LOG_E("SDIO block device %s not found", SDIO_UTEST_DEVICE_NAME); + return -RT_ERROR; + } + + if (sdio_test_dev->type != RT_Device_Class_Block) + { + LOG_E("Device %s is not a block device", SDIO_UTEST_DEVICE_NAME); + return -RT_EINVAL; + } + + rt_memset(&sdio_test_geome, 0, sizeof(sdio_test_geome)); + result = rt_device_control(sdio_test_dev, + RT_DEVICE_CTRL_BLK_GETGEOME, + &sdio_test_geome); + if (result != RT_EOK) + { + LOG_E("Get SDIO geometry failed, result: %d", result); + return result; + } + + if (sdio_test_geome.bytes_per_sector != SDIO_UTEST_SECTOR_SIZE) + { + LOG_E("Unsupported SDIO sector size: %u", + sdio_test_geome.bytes_per_sector); + return -RT_EINVAL; + } + + if (sdio_test_geome.sector_count < SDIO_UTEST_SECTOR_COUNT) + { + LOG_E("SDIO device is too small: %lu sectors", + (unsigned long)sdio_test_geome.sector_count); + return -RT_EINVAL; + } + + if (SDIO_UTEST_START_SECTOR < 0 || + (rt_uint64_t)SDIO_UTEST_START_SECTOR + SDIO_UTEST_SECTOR_COUNT > + sdio_test_geome.sector_count) + { + LOG_E("SDIO test range is out of bounds: start=%ld, count=%u", + (long)SDIO_UTEST_START_SECTOR, + (unsigned int)SDIO_UTEST_SECTOR_COUNT); + return -RT_EINVAL; + } + + sdio_test_buf_size = sdio_test_geome.bytes_per_sector * + SDIO_UTEST_SECTOR_COUNT; + sdio_test_write_buf = (rt_uint8_t *)rt_malloc(sdio_test_buf_size); + sdio_test_read_buf = (rt_uint8_t *)rt_malloc(sdio_test_buf_size); + if (sdio_test_write_buf == RT_NULL || sdio_test_read_buf == RT_NULL) + { + LOG_E("Allocate SDIO test buffer failed, size: %u", + (unsigned int)sdio_test_buf_size); + sdio_test_free_buffers(); + return -RT_ENOMEM; + } + + result = rt_device_open(sdio_test_dev, RT_DEVICE_OFLAG_RDWR); + if (result != RT_EOK) + { + LOG_E("Open SDIO device %s failed, result: %d", + SDIO_UTEST_DEVICE_NAME, result); + sdio_test_free_buffers(); + return result; + } + sdio_test_opened = RT_TRUE; + + LOG_I("SDIO test device=%s, sector_count=%lu, start=%ld, count=%u", + SDIO_UTEST_DEVICE_NAME, + (unsigned long)sdio_test_geome.sector_count, + (long)SDIO_UTEST_START_SECTOR, + (unsigned int)SDIO_UTEST_SECTOR_COUNT); + + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_err_t result = RT_EOK; + + if (sdio_test_opened) + { + result = rt_device_close(sdio_test_dev); + sdio_test_opened = RT_FALSE; + } + + sdio_test_free_buffers(); + sdio_test_dev = RT_NULL; + rt_memset(&sdio_test_geome, 0, sizeof(sdio_test_geome)); + + return result; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(sdio_test_check_geometry); + UTEST_UNIT_RUN(sdio_test_single_sector); + UTEST_UNIT_RUN(sdio_test_start_sector); + UTEST_UNIT_RUN(sdio_test_end_sector); + UTEST_UNIT_RUN(sdio_test_reopen); +} + +UTEST_TC_EXPORT(testcase, + "components.drivers.sdio.read_write", + utest_tc_init, + utest_tc_cleanup, + 30); + +#endif /* RT_UTEST_SDIO */ diff --git a/components/utilities/utest/Kconfig b/components/utilities/utest/Kconfig index f2bb5f6f887..bf42356fd15 100644 --- a/components/utilities/utest/Kconfig +++ b/components/utilities/utest/Kconfig @@ -18,6 +18,7 @@ menu "RT-Thread Utestcases" rsource "../../../components/drivers/ipc/utest/Kconfig" rsource "../../../components/drivers/pin/utest/Kconfig" rsource "../../../components/drivers/serial/utest/Kconfig" + rsource "../../../components/drivers/sdio/utest/Kconfig" rsource "../../../components/drivers/smp_call/utest/Kconfig" rsource "../../../components/drivers/spi/utest/Kconfig" endmenu