diff --git a/headers/private/drivers/mmc.h b/headers/private/drivers/mmc.h index 8d3780e122..e8434c4171 100644 --- a/headers/private/drivers/mmc.h +++ b/headers/private/drivers/mmc.h @@ -49,6 +49,11 @@ enum SD_COMMANDS { SD_WRITE_SINGLE_BLOCK = 24, SD_WRITE_MULTIPLE_BLOCKS = 25, + // Erase commands, class 5 + SD_ERASE_WR_BLK_START = 32, + SD_ERASE_WR_BLK_END = 33, + SD_ERASE = 38, + // Application specific commands, class 8 SD_APP_CMD = 55, diff --git a/headers/private/kernel/util/fs_trim_support.h b/headers/private/kernel/util/fs_trim_support.h index 22e6f9ac41..317929faa9 100644 --- a/headers/private/kernel/util/fs_trim_support.h +++ b/headers/private/kernel/util/fs_trim_support.h @@ -6,7 +6,9 @@ #define _FS_TRIM_SUPPORT_H +#include #include +#include #include #include diff --git a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp index ae9831496f..748bacdf5f 100644 --- a/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp +++ b/src/add-ons/kernel/busses/mmc/sdhci_pci.cpp @@ -213,6 +213,7 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) replyType = Command::kR6Type; break; case SD_SELECT_DESELECT_CARD: + case SD_ERASE: replyType = Command::kR1bType; break; case SD_SEND_IF_COND: @@ -225,6 +226,8 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response) replyType = Command::kR1Type | Command::kDataPresent; break; case SD_APP_CMD: + case SD_ERASE_WR_BLK_START: + case SD_ERASE_WR_BLK_END: case SD_SET_BUS_WIDTH: // SD Application command replyType = Command::kR1Type; break; diff --git a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp index 2fec95e5aa..66a66b5ce4 100644 --- a/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/mmc/mmc_disk.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2020 Haiku, Inc. All rights reserved. + * Copyright 2018-2021 Haiku, Inc. All rights reserved. * Copyright 2020, Viveris Technologies. * Distributed under the terms of the MIT License. * @@ -23,8 +23,10 @@ #include #include #include +#include + +#include -// #include #define TRACE_MMC_DISK #ifdef TRACE_MMC_DISK @@ -39,6 +41,9 @@ #define MMC_DISK_DEVICE_MODULE_NAME "drivers/disk/mmc/mmc_disk/device_v1" #define MMC_DEVICE_ID_GENERATOR "mmc/device_id" + +static const uint32 kBlockSize = 512; // FIXME get it from the CSD + static device_manager_info* sDeviceManager; @@ -237,7 +242,6 @@ mmc_disk_init_driver(device_node* node, void** cookie) else info->flags = kIoCommandOffsetAsSectors; - static const uint32 kBlockSize = 512; // FIXME get it from the CSD status_t error; static const uint32 kDMAResourceBufferCount = 16; @@ -475,6 +479,73 @@ mmc_block_io(void* cookie, io_request* request) } +static status_t +mmc_block_trim(mmc_disk_driver_info* info, fs_trim_data* trimData) +{ + enum { + kEraseModeErase = 0, // force to actually erase the data + kEraseModeDiscard = 1, + // just mark the data as unused for internal wear leveling + // algorithms + kEraseModeFullErase = 2, // erase the whole card + }; + TRACE("trim_device()\n"); + + uint64 trimmedSize = 0; + status_t result = B_OK; + for (uint32 i = 0; i < trimData->range_count; i++) { + off_t offset = trimData->ranges[i].offset; + off_t length = trimData->ranges[i].size; + + // Round up offset and length to multiple of the sector size + // The offset is rounded up, so some space may be left + // (not trimmed) at the start of the range. + offset = ROUNDUP(offset, kBlockSize); + // Adjust the length for the possibly skipped range + length -= trimData->ranges[i].offset - offset; + // The length is rounded down, so some space at the end may also + // be left (not trimmed). + length &= ~(kBlockSize - 1); + + if (length == 0) { + trimmedSize += trimData->ranges[i].size; + continue; + } + + TRACE("trim %" B_PRIdOFF " bytes from %" B_PRIdOFF "\n", + length, offset); + + ASSERT(offset % kBlockSize == 0); + ASSERT(length % kBlockSize == 0); + + if (info->flags & kIoCommandOffsetAsSectors) { + offset /= kBlockSize; + length /= kBlockSize; + } + + uint32_t response; + result = info->mmc->execute_command(info->parent, info->parentCookie, + info->rca, SD_ERASE_WR_BLK_START, offset, &response); + if (result != B_OK) + break; + result = info->mmc->execute_command(info->parent, info->parentCookie, + info->rca, SD_ERASE_WR_BLK_END, offset + length, &response); + if (result != B_OK) + break; + result = info->mmc->execute_command(info->parent, info->parentCookie, + info->rca, SD_ERASE, kEraseModeDiscard, &response); + if (result != B_OK) + break; + + trimmedSize += trimData->ranges[i].size; + } + + trimData->trimmed_size = trimmedSize; + + return result; +} + + static status_t mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) { @@ -550,6 +621,13 @@ mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length) return user_memcpy(buffer, &iconData, sizeof(device_icon)); } + case B_TRIM_DEVICE: + { + // We know the buffer is kernel-side because it has been + // preprocessed in devfs + return mmc_block_trim(info, (fs_trim_data*)buffer); + } + /*case B_FLUSH_DRIVE_CACHE: return synchronize_cache(info);*/ }