/* * Copyright (C) 2017 HAW-Hamburg * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. * */ /** * @ingroup drivers_mtd_sdcard * @{ * * @file * @brief Driver for using sdcard_spi via mtd interface * * @author Michel Rottleuthner * * @} */ #define ENABLE_DEBUG 0 #include "debug.h" #include "mtd.h" #include "mtd_sdcard.h" #include "sdcard_spi.h" #include "sdcard_spi_internal.h" #include "kernel_defines.h" #include #include #include #define MIN(a, b) ((a) > (b) ? (b) : (a)) static int mtd_sdcard_init(mtd_dev_t *dev) { DEBUG("mtd_sdcard_init\n"); mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev; if ((mtd_sd->sd_card->init_done == true) || (sdcard_spi_init(mtd_sd->sd_card, mtd_sd->params) == 0)) { /* erasing whole sectors is handled internally by the card so you can delete single blocks (i.e. pages) */ dev->pages_per_sector = 1; dev->sector_count = sdcard_spi_get_sector_count(mtd_sd->sd_card); /* sdcard_spi always uses the fixed block size of SD-HC cards */ dev->page_size = SD_HC_BLOCK_SIZE; return 0; } return -EIO; } static int mtd_sdcard_read_page(mtd_dev_t *dev, void *buff, uint32_t page, uint32_t offset, uint32_t size) { sd_rw_response_t err; mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev; DEBUG("mtd_sdcard_read_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n", page, offset, size); if (offset || size % SD_HC_BLOCK_SIZE) { #if IS_USED(MODULE_MTD_WRITE_PAGE) if (dev->work_area == NULL) { DEBUG("mtd_sdcard_read_page: no work area\n"); return -ENOTSUP; } sdcard_spi_read_blocks(mtd_sd->sd_card, page, dev->work_area, SD_HC_BLOCK_SIZE, 1, &err); if (err != SD_RW_OK) { return -EIO; } size = MIN(size, SD_HC_BLOCK_SIZE - offset); DEBUG("mtd_sdcard_read_page: read %" PRIu32 " bytes at offset %" PRIu32 "\n", size, offset); memcpy(buff, (uint8_t *)dev->work_area + offset, size); return size; #else return -ENOTSUP; #endif } sdcard_spi_read_blocks(mtd_sd->sd_card, page, buff, SD_HC_BLOCK_SIZE, size / SD_HC_BLOCK_SIZE, &err); if (err != SD_RW_OK) { return -EIO; } return size; } static int mtd_sdcard_write_page(mtd_dev_t *dev, const void *buff, uint32_t page, uint32_t offset, uint32_t size) { sd_rw_response_t err; mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev; DEBUG("mtd_sdcard_write_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n", page, offset, size); if (offset || size % SD_HC_BLOCK_SIZE) { #if IS_USED(MODULE_MTD_WRITE_PAGE) if (dev->work_area == NULL) { DEBUG("mtd_sdcard_write_page: no work area\n"); return -ENOTSUP; } sdcard_spi_read_blocks(mtd_sd->sd_card, page, dev->work_area, SD_HC_BLOCK_SIZE, 1, &err); if (err != SD_RW_OK) { return -EIO; } size = MIN(size, SD_HC_BLOCK_SIZE - offset); DEBUG("mtd_sdcard_write_page: write %" PRIu32 " bytes at offset %" PRIu32 "\n", size, offset); memcpy((uint8_t *)dev->work_area + offset, buff, size); sdcard_spi_write_blocks(mtd_sd->sd_card, page, dev->work_area, SD_HC_BLOCK_SIZE, 1, &err); #else return -ENOTSUP; #endif } else { sdcard_spi_write_blocks(mtd_sd->sd_card, page, buff, SD_HC_BLOCK_SIZE, size / SD_HC_BLOCK_SIZE, &err); } if (err != SD_RW_OK) { DEBUG("mtd_sdcard_write_page: error %d\n", err); return -EIO; } return size; } static int mtd_sdcard_erase_sector(mtd_dev_t *dev, uint32_t sector, uint32_t count) { #if IS_ACTIVE(CONFIG_MTD_SDCARD_ERASE) && IS_USED(MODULE_MTD_WRITE_PAGE) mtd_sdcard_t *mtd_sd = (mtd_sdcard_t*)dev; DEBUG("mtd_sdcard_erase_sector: sector: %" PRIu32 " count: %" PRIu32 "\n", sector, count); if (dev->work_area == NULL) { DEBUG("mtd_sdcard_erase_sector: no work area\n"); return -ENOTSUP; } memset(dev->work_area, 0, SD_HC_BLOCK_SIZE); while (count) { sd_rw_response_t err; sdcard_spi_write_blocks(mtd_sd->sd_card, sector, dev->work_area, SD_HC_BLOCK_SIZE, 1, &err); if (err != SD_RW_OK) { DEBUG("mtd_sdcard_erase_sector: error %d\n", err); return -EIO; } --count; ++sector; } #else (void)dev; (void)sector; (void)count; if (IS_ACTIVE(CONFIG_MTD_SDCARD_ERASE)) { return -ENOTSUP; } #endif return 0; } static int mtd_sdcard_power(mtd_dev_t *dev, enum mtd_power_state power) { (void)dev; (void)power; /* TODO: implement power down of sdcard in sdcard_spi (make use of sdcard_spi_params_t.power pin) */ return -ENOTSUP; /* currently not supported */ } static int mtd_sdcard_read(mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size) { int res = mtd_sdcard_read_page(dev, buff, addr / SD_HC_BLOCK_SIZE, addr % SD_HC_BLOCK_SIZE, size); if (res < 0) { return res; } if (res == (int)size) { return 0; } return -EOVERFLOW; } static int mtd_sdcard_write(mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size) { int res = mtd_sdcard_write_page(dev, buff, addr / SD_HC_BLOCK_SIZE, addr % SD_HC_BLOCK_SIZE, size); if (res < 0) { return res; } if (res == (int)size) { return 0; } return -EOVERFLOW; } const mtd_desc_t mtd_sdcard_driver = { .init = mtd_sdcard_init, .read = mtd_sdcard_read, .read_page = mtd_sdcard_read_page, .write = mtd_sdcard_write, .write_page = mtd_sdcard_write_page, .erase_sector = mtd_sdcard_erase_sector, .power = mtd_sdcard_power, };