* Copyright 2021 David Sebek, dasebek@gmail.com
* Copyright 2004-2013 Haiku, Inc.
* Copyright 2002-2003 Thomas Kurschel
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <string.h>
#include <AutoDeleter.h>
#include "scsi_periph_int.h"
#define UNMAP_MAX_LBA_VALUE UINT64_MAX
#define UNMAP_MAX_BLOCK_COUNT_VALUE UINT32_MAX
#define UNMAP_MAX_DESCRIPTORS 4095
#define UNMAP_DEFAULT_DESCRIPTORS 255
#define WS16_MAX_LBA_VALUE UINT64_MAX
#define WS16_MAX_BLOCK_COUNT_VALUE UINT32_MAX
#define WS10_MAX_LBA_VALUE UINT32_MAX
#define WS10_MAX_BLOCK_COUNT_VALUE UINT16_MAX
struct CapacityInfo {
bool capacityFilled;
uint64 lastLba;
uint32 blockSize;
uint32 physicalBlockSize;
bool provisioningFilled;
bool lbpme;
bool lbprz;
};
struct UnmapSupport {
bool commandSupportFilled;
bool unmapSupported;
bool ws16Supported;
bool ws10Supported;
bool blockLimitsFilled;
uint32 maxUnmapLbaCount;
uint32 maxUnmapDescriptorCount;
uint64 maxWritesameLength;
};
static bool
prefer_read_capacity_16(scsi_periph_device_info* device)
{
const scsi_res_inquiry* inquiryData = NULL;
size_t inquiryDataLength;
if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
(const void**)&inquiryData, &inquiryDataLength, true) != B_OK
|| inquiryDataLength != sizeof(*inquiryData)) {
return false;
}
if (inquiryData->protect)
return true;
if (inquiryData->ansi_version > 0x04 )
return true;
return false;
}
static bool
vpd_pages_supported(scsi_periph_device_info* device)
{
const scsi_res_inquiry* inquiryData = NULL;
size_t inquiryDataLength;
if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
(const void**)&inquiryData, &inquiryDataLength, true) != B_OK
|| inquiryDataLength != sizeof(*inquiryData)) {
return false;
}
if (inquiryData->ansi_version >= 0x04 )
return true;
return false;
}
static status_t
read_capacity_10(scsi_periph_device_info* device, scsi_ccb* request,
CapacityInfo* capacityInfo)
{
capacityInfo->capacityFilled = false;
capacityInfo->provisioningFilled = false;
scsi_res_read_capacity capacityResult;
memset(&capacityResult, 0, sizeof(capacityResult));
scsi_cmd_read_capacity* cmd = (scsi_cmd_read_capacity*)request->cdb;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_READ_CAPACITY;
request->flags = SCSI_DIR_IN;
request->cdb_length = sizeof(*cmd);
request->sort = -1;
request->timeout = device->std_timeout;
request->data = (uint8*)&capacityResult;
request->data_length = sizeof(capacityResult);
request->sg_list = NULL;
status_t res = periph_safe_exec(device, request);
if (res == B_OK && request->data_resid == 0) {
capacityInfo->capacityFilled = true;
capacityInfo->lastLba
= (uint32)B_BENDIAN_TO_HOST_INT32(capacityResult.lba);
capacityInfo->blockSize
= B_BENDIAN_TO_HOST_INT32(capacityResult.block_size);
capacityInfo->physicalBlockSize = capacityInfo->blockSize;
}
return res;
}
static status_t
read_capacity_16(scsi_periph_device_info* device, scsi_ccb* request,
CapacityInfo* capacityInfo)
{
capacityInfo->capacityFilled = false;
capacityInfo->provisioningFilled = false;
scsi_res_read_capacity_long capacityLongResult;
memset(&capacityLongResult, 0, sizeof(capacityLongResult));
scsi_cmd_read_capacity_long* cmd
= (scsi_cmd_read_capacity_long*)request->cdb;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_SERVICE_ACTION_IN;
cmd->service_action = SCSI_SAI_READ_CAPACITY_16;
cmd->alloc_length = B_HOST_TO_BENDIAN_INT32(sizeof(capacityLongResult));
request->flags = SCSI_DIR_IN;
request->cdb_length = sizeof(*cmd);
request->sort = -1;
request->timeout = device->std_timeout;
request->data = (uint8*)&capacityLongResult;
request->data_length = sizeof(capacityLongResult);
request->sg_list = NULL;
status_t res = periph_safe_exec(device, request);
if (res == B_OK && request->data_resid
<= (int32)sizeof(scsi_res_read_capacity_long) - 12) {
capacityInfo->capacityFilled = true;
capacityInfo->lastLba
= B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba);
capacityInfo->blockSize
= B_BENDIAN_TO_HOST_INT32(capacityLongResult.block_size);
capacityInfo->physicalBlockSize = capacityInfo->blockSize
* (1 << capacityLongResult.logical_blocks_per_physical_block_exponent);
}
if (res == B_OK && request->data_resid
<= (int32)sizeof(scsi_res_read_capacity_long) - 15) {
capacityInfo->provisioningFilled = true;
capacityInfo->lbpme = capacityLongResult.lbpme;
capacityInfo->lbprz = capacityLongResult.lbprz;
}
return res;
}
static status_t
get_unmap_commands(scsi_periph_device_info* device, scsi_ccb* request,
UnmapSupport* unmapSupport)
{
unmapSupport->commandSupportFilled = false;
scsi_page_lb_provisioning vpdProvisioning;
memset(&vpdProvisioning, 0, sizeof(vpdProvisioning));
status_t vpdStatus = vpd_page_get(device, request,
SCSI_PAGE_LB_PROVISIONING, &vpdProvisioning, sizeof(vpdProvisioning));
if (vpdStatus == B_OK
&& request->data_resid <= (int32)sizeof(scsi_page_lb_provisioning) - 6
&& vpdProvisioning.page_code == SCSI_PAGE_LB_PROVISIONING
&& B_BENDIAN_TO_HOST_INT16(vpdProvisioning.page_length) >= 2) {
unmapSupport->commandSupportFilled = true;
unmapSupport->unmapSupported = vpdProvisioning.lbpu;
unmapSupport->ws16Supported = vpdProvisioning.lbpws;
unmapSupport->ws10Supported = vpdProvisioning.lbpws10;
}
if (vpdStatus == B_BAD_VALUE)
return B_ERROR;
return vpdStatus;
}
static status_t
get_unmap_limits(scsi_periph_device_info* device, scsi_ccb* request,
UnmapSupport* unmapSupport)
{
unmapSupport->blockLimitsFilled = false;
scsi_page_block_limits vpdBlockLimits;
memset(&vpdBlockLimits, 0, sizeof(vpdBlockLimits));
status_t vpdStatus = vpd_page_get(device, request,
SCSI_PAGE_BLOCK_LIMITS, &vpdBlockLimits, sizeof(vpdBlockLimits));
if (vpdStatus == B_OK
&& request->data_resid <= (int32)sizeof(scsi_page_block_limits) - 44
&& vpdBlockLimits.page_code == SCSI_PAGE_BLOCK_LIMITS
&& B_BENDIAN_TO_HOST_INT16(vpdBlockLimits.page_length) == 0x3c) {
unmapSupport->blockLimitsFilled = true;
unmapSupport->maxUnmapLbaCount = B_BENDIAN_TO_HOST_INT32(
vpdBlockLimits.max_unmap_lba_count);
unmapSupport->maxUnmapDescriptorCount = B_BENDIAN_TO_HOST_INT32(
vpdBlockLimits.max_unmap_blk_count);
unmapSupport->maxWritesameLength = B_BENDIAN_TO_HOST_INT64(
vpdBlockLimits.max_write_same_length);
}
if (vpdStatus == B_BAD_VALUE)
return B_ERROR;
return vpdStatus;
}
static void
determine_unmap_support(const UnmapSupport* unmapSupport,
enum trim_command* unmapCommand, uint32* maxLbaCount,
uint32* maxDescriptorCount)
{
#ifdef DEBUG_TRIM
if (unmapSupport->commandSupportFilled)
dprintf("TRIM: device reports (LBP VPD): LBPU = %d, LBPWS = %d,"
" LBPWS10 = %d\n", unmapSupport->unmapSupported,
unmapSupport->ws16Supported, unmapSupport->ws10Supported);
else
dprintf("TRIM: could not get the LBP VPD of the device\n");
if (unmapSupport->blockLimitsFilled)
dprintf("TRIM: device reports (Block Limits VPD):"
"\nTRIM: MAXIMUM UNMAP LBA COUNT = %" B_PRIu32
"\nTRIM: MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT = %" B_PRIu32
"\nTRIM: MAXIMUM WRITESAME LENGTH = %" B_PRIu64 "\n",
unmapSupport->maxUnmapLbaCount,
unmapSupport->maxUnmapDescriptorCount,
unmapSupport->maxWritesameLength);
else
dprintf("TRIM: could not get Block Limits VPD of the device\n");
#endif
*unmapCommand = TRIM_NONE;
*maxLbaCount = 0;
*maxDescriptorCount = 0;
if (!unmapSupport->commandSupportFilled
|| !unmapSupport->blockLimitsFilled)
return;
if (unmapSupport->unmapSupported
&& unmapSupport->maxUnmapLbaCount > 0
&& unmapSupport->maxUnmapDescriptorCount > 0) {
*unmapCommand = TRIM_UNMAP;
*maxLbaCount = unmapSupport->maxUnmapLbaCount;
if (unmapSupport->maxUnmapDescriptorCount == UINT32_MAX
|| unmapSupport->maxUnmapDescriptorCount > UNMAP_MAX_DESCRIPTORS) {
*maxDescriptorCount = UNMAP_DEFAULT_DESCRIPTORS;
} else {
*maxDescriptorCount = unmapSupport->maxUnmapDescriptorCount;
}
}
if (*unmapCommand == TRIM_NONE && unmapSupport->ws16Supported) {
uint64 maxLength = unmapSupport->maxWritesameLength;
if (maxLength == 0) {
if (unmapSupport->maxUnmapLbaCount > 0)
maxLength = unmapSupport->maxUnmapLbaCount;
else
maxLength = WS16_MAX_BLOCK_COUNT_VALUE;
}
*unmapCommand = TRIM_WRITESAME16;
*maxLbaCount = min_c(maxLength, WS16_MAX_BLOCK_COUNT_VALUE);
*maxDescriptorCount = 1;
}
if (*unmapCommand == TRIM_NONE && unmapSupport->ws10Supported) {
uint64 maxLength = unmapSupport->maxWritesameLength;
if (maxLength == 0) {
if (unmapSupport->maxUnmapLbaCount > 0)
maxLength = unmapSupport->maxUnmapLbaCount;
else
maxLength = WS10_MAX_BLOCK_COUNT_VALUE;
}
*unmapCommand = TRIM_WRITESAME10;
*maxLbaCount = min_c(maxLength, WS10_MAX_BLOCK_COUNT_VALUE);
*maxDescriptorCount = 1;
}
}
status_t
periph_check_capacity(scsi_periph_device_info* device, scsi_ccb* request)
{
CapacityInfo capacityInfo = {0};
status_t res;
SHOW_FLOW(3, "%p, %p", device, request);
if (device->callbacks->set_capacity == NULL)
return B_OK;
if (prefer_read_capacity_16(device)) {
SHOW_FLOW0(3, "READ CAPACITY 16 tried first");
res = read_capacity_16(device, request, &capacityInfo);
if (res == B_ERROR) {
SHOW_FLOW0(3, "READ CAPACITY 16 failed, trying READ CAPACITY 10");
res = read_capacity_10(device, request, &capacityInfo);
}
} else {
SHOW_FLOW0(3, "READ CAPACITY 10 tried first");
res = read_capacity_10(device, request, &capacityInfo);
if (res == B_OK && capacityInfo.capacityFilled
&& capacityInfo.lastLba == UINT32_MAX) {
SHOW_FLOW0(3, "Device is too large, trying READ CAPACITY 16");
res = read_capacity_16(device, request, &capacityInfo);
}
}
uint64 capacity;
uint32 blockSize, physicalBlockSize;
if (capacityInfo.capacityFilled) {
capacity = capacityInfo.lastLba + 1;
blockSize = capacityInfo.blockSize;
physicalBlockSize = capacityInfo.physicalBlockSize;
} else {
capacity = 0;
blockSize = 0;
physicalBlockSize = 0;
}
enum trim_command unmapCommand = TRIM_NONE;
uint32 maxLbaCount = 0;
uint32 maxDescriptorCount = 0;
if (capacityInfo.provisioningFilled
&& capacityInfo.lbpme
&& vpd_pages_supported(device)) {
UnmapSupport unmapSupport = {0};
if (res == B_OK) {
status_t vpdStatus = get_unmap_commands(device, request,
&unmapSupport);
if (vpdStatus != B_OK && vpdStatus != B_ERROR)
res = vpdStatus;
}
if (res == B_OK) {
status_t vpdStatus = get_unmap_limits(device, request,
&unmapSupport);
if (vpdStatus != B_OK && vpdStatus != B_ERROR)
res = vpdStatus;
}
determine_unmap_support(&unmapSupport, &unmapCommand,
&maxLbaCount, &maxDescriptorCount);
if (maxLbaCount == 0 || maxDescriptorCount == 0)
unmapCommand = TRIM_NONE;
}
if (res == B_DEV_MEDIA_CHANGED) {
SHOW_FLOW0(3, "ignore result because medium change");
return B_DEV_MEDIA_CHANGED;
}
if (res == B_OK && !capacityInfo.capacityFilled)
res = B_ERROR;
SHOW_FLOW(3, "capacity = %" B_PRIu64 ", block_size = %" B_PRIu32
" (%sreported)", capacity, blockSize,
capacityInfo.capacityFilled ? "" : "not ");
SHOW_INFO(1, "TRIM: Setting trim support to %s",
unmapCommand == TRIM_NONE ? "disabled"
: unmapCommand == TRIM_UNMAP ? "UNMAP"
: unmapCommand == TRIM_WRITESAME16 ? "WRITE SAME (16)"
: unmapCommand == TRIM_WRITESAME10 ? "WRITE SAME (10)"
: "unknown");
SHOW_FLOW(3, "TRIM: Block limits: size = %" B_PRIu32
", descriptors = %" B_PRIu32, maxLbaCount, maxDescriptorCount);
mutex_lock(&device->mutex);
device->unmap_command = unmapCommand;
device->max_unmap_lba_count = maxLbaCount;
device->max_unmap_descriptor_count = maxDescriptorCount;
device->block_size = blockSize;
device->physical_block_size = physicalBlockSize;
device->callbacks->set_capacity(device->periph_device,
capacity, blockSize, physicalBlockSize);
if( device->byte2blk_shift < 0 ) {
// this may be too restrictive...
device->capacity = -1;
return ERR_DEV_GENERAL;
}*/
mutex_unlock(&device->mutex);
SHOW_FLOW(3, "done (%s)", strerror(res));
return res;
}
static status_t
trim_unmap(scsi_periph_device_info* device, scsi_ccb* request,
scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
{
uint64 maxLength = UNMAP_MAX_BLOCK_COUNT_VALUE;
uint64 maxBlocksInRequest = device->max_unmap_lba_count;
uint32 maxDescriptors = device->max_unmap_descriptor_count;
*trimmedBlocks = 0;
size_t expectedDescriptorCount = 0;
for (uint32 i = 0; i < rangeCount; i++) {
expectedDescriptorCount += ranges[i].size / maxLength;
if (ranges[i].size % maxLength != 0)
expectedDescriptorCount++;
}
expectedDescriptorCount = min_c(expectedDescriptorCount, maxDescriptors);
size_t unmapListAllocatedSize = (expectedDescriptorCount - 1)
* sizeof(scsi_unmap_block_descriptor)
+ sizeof(scsi_unmap_parameter_list);
scsi_unmap_parameter_list* unmapList
= (scsi_unmap_parameter_list*)malloc(unmapListAllocatedSize);
if (unmapList == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(unmapList);
status_t status = B_OK;
uint32 descriptorIndex = 0;
uint64 trimmedBlocksInRequest = 0;
memset(unmapList, 0, unmapListAllocatedSize);
for (uint32 i = 0; i < rangeCount; i++) {
uint64 lba = ranges[i].lba;
uint64 length = ranges[i].size;
if (length == 0)
continue;
if (lba > UNMAP_MAX_LBA_VALUE) {
SHOW_ERROR0(1, "LBA value is too large!"
" This unmap range will be skipped.");
continue;
}
while (length > 0) {
uint64 trimLength = min_c(length, maxLength);
trimLength = min_c(trimLength,
maxBlocksInRequest - trimmedBlocksInRequest);
unmapList->blocks[descriptorIndex].lba
= B_HOST_TO_BENDIAN_INT64(lba);
unmapList->blocks[descriptorIndex].block_count
= B_HOST_TO_BENDIAN_INT32(trimLength);
descriptorIndex++;
trimmedBlocksInRequest += trimLength;
if (descriptorIndex >= maxDescriptors
|| descriptorIndex >= expectedDescriptorCount
|| descriptorIndex >= UNMAP_MAX_DESCRIPTORS
|| trimmedBlocksInRequest >= maxBlocksInRequest
|| (i == rangeCount - 1 && length <= maxLength))
{
uint16 unmapListSize = (descriptorIndex - 1)
* sizeof(scsi_unmap_block_descriptor)
+ sizeof(scsi_unmap_parameter_list);
unmapList->data_length = B_HOST_TO_BENDIAN_INT16(unmapListSize
- offsetof(scsi_unmap_parameter_list, block_data_length));
unmapList->block_data_length
= B_HOST_TO_BENDIAN_INT16(unmapListSize
- offsetof(scsi_unmap_parameter_list, blocks));
scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_UNMAP;
cmd->length = B_HOST_TO_BENDIAN_INT16(unmapListSize);
request->flags = SCSI_DIR_OUT;
request->cdb_length = sizeof(*cmd);
request->sort = B_BENDIAN_TO_HOST_INT64(
unmapList->blocks[0].lba);
request->timeout = device->std_timeout;
request->data = (uint8*)unmapList;
request->data_length = unmapListSize;
request->sg_list = NULL;
SHOW_FLOW(3, "UNMAP data used %" B_PRIu16
" of %" B_PRIuSIZE " allocated bytes",
unmapListSize, unmapListAllocatedSize);
#ifdef DEBUG_TRIM
uint16 scsiRangeCount = (uint16)B_BENDIAN_TO_HOST_INT16(
unmapList->block_data_length)
/ sizeof(scsi_unmap_block_descriptor);
uint64 count = 0;
dprintf("TRIM: SCSI: sending an UNMAP command to"
" the device (blocks):\n");
for (uint16 i = 0; i < scsiRangeCount; i++) {
dprintf("[%3" B_PRIu16 "] %" B_PRIu64 " : %" B_PRIu32 "\n",
i, (uint64)B_BENDIAN_TO_HOST_INT64(
unmapList->blocks[i].lba),
(uint32)B_BENDIAN_TO_HOST_INT32(
unmapList->blocks[i].block_count));
count += (uint32)B_BENDIAN_TO_HOST_INT32(
unmapList->blocks[i].block_count);
}
if (device->max_unmap_lba_count >= count)
dprintf("TRIM: SCSI: Previous UNMAP command would fit %"
B_PRIu64 " more LBAs\n",
device->max_unmap_lba_count - count);
else
dprintf("TRIM: SCSI: Previous UNMAP ranges exceed the"
" device limit!\n");
#endif
status = periph_safe_exec(device, request);
if (status == B_DEV_READ_ERROR)
return B_DEV_WRITE_ERROR;
else if (status != B_OK)
return status;
*trimmedBlocks += trimmedBlocksInRequest;
descriptorIndex = 0;
trimmedBlocksInRequest = 0;
memset(unmapList, 0, unmapListSize);
}
length -= trimLength;
lba += trimLength;
}
}
return status;
}
static status_t
trim_writesame16(scsi_periph_device_info* device, scsi_ccb* request,
scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
{
status_t status = B_OK;
*trimmedBlocks = 0;
for (uint32 i = 0; i < rangeCount; i++) {
uint64 lba = ranges[i].lba;
uint64 length = ranges[i].size;
if (length == 0)
continue;
if (lba > WS16_MAX_LBA_VALUE) {
SHOW_ERROR0(1, "LBA value is too large!"
" This unmap range will be skipped.");
continue;
}
uint64 maxLength = min_c(device->max_unmap_lba_count,
WS16_MAX_BLOCK_COUNT_VALUE);
while (length > 0) {
uint64 trimLength = min_c(length, maxLength);
if (trimLength == 0) {
SHOW_ERROR0(1,
"Error: Length of zero in WRITE SAME (16) detected");
break;
}
void* block = malloc(device->block_size);
if (block == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(block);
memset(block, 0, device->block_size);
scsi_cmd_wsame_16* cmd = (scsi_cmd_wsame_16*)request->cdb;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_WRITE_SAME_16;
cmd->unmap = 1;
cmd->lba = B_HOST_TO_BENDIAN_INT64(lba);
cmd->length = B_HOST_TO_BENDIAN_INT32(trimLength);
request->flags = SCSI_DIR_OUT;
request->cdb_length = sizeof(*cmd);
request->sort = lba;
request->timeout = device->std_timeout;
request->data = (uint8*)block;
request->data_length = device->block_size;
request->sg_list = NULL;
#ifdef DEBUG_TRIM
dprintf("TRIM: SCSI: sending a WRITE SAME (16) command to"
" the device (blocks):\n");
dprintf("%" B_PRIu64 " : %" B_PRIu32 "\n",
(uint64)B_BENDIAN_TO_HOST_INT64(cmd->lba),
(uint32)B_BENDIAN_TO_HOST_INT32(cmd->length));
#endif
status = periph_safe_exec(device, request);
if (status == B_DEV_READ_ERROR)
return B_DEV_WRITE_ERROR;
else if (status != B_OK)
return status;
*trimmedBlocks += trimLength;
length -= trimLength;
lba += trimLength;
}
}
return status;
}
static status_t
trim_writesame10(scsi_periph_device_info* device, scsi_ccb* request,
scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
{
status_t status = B_OK;
*trimmedBlocks = 0;
for (uint32 i = 0; i < rangeCount; i++) {
uint64 lba = ranges[i].lba;
uint64 length = ranges[i].size;
if (length == 0)
continue;
if (lba > WS10_MAX_LBA_VALUE) {
SHOW_ERROR0(1, "LBA value is too large!"
" This unmap range will be skipped.");
continue;
}
uint64 maxLength = min_c(device->max_unmap_lba_count,
WS10_MAX_BLOCK_COUNT_VALUE);
while (length > 0) {
uint64 trimLength = min_c(length, maxLength);
if (trimLength == 0) {
SHOW_ERROR0(1,
"Error: Length of zero in WRITE SAME (10) detected");
break;
}
void* block = malloc(device->block_size);
if (block == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(block);
memset(block, 0, device->block_size);
scsi_cmd_wsame_10* cmd = (scsi_cmd_wsame_10*)request->cdb;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_WRITE_SAME_10;
cmd->unmap = 1;
cmd->lba = B_HOST_TO_BENDIAN_INT32(lba);
cmd->length = B_HOST_TO_BENDIAN_INT16(trimLength);
request->flags = SCSI_DIR_OUT;
request->cdb_length = sizeof(*cmd);
request->sort = lba;
request->timeout = device->std_timeout;
request->data = (uint8*)block;
request->data_length = device->block_size;
request->sg_list = NULL;
#ifdef DEBUG_TRIM
dprintf("TRIM: SCSI: sending a WRITE SAME (10) command to"
" the device (blocks):\n");
dprintf("%" B_PRIu32 " : %" B_PRIu16 "\n",
(uint32)B_BENDIAN_TO_HOST_INT32(cmd->lba),
(uint16)B_BENDIAN_TO_HOST_INT16(cmd->length));
#endif
status = periph_safe_exec(device, request);
if (status == B_DEV_READ_ERROR)
return B_DEV_WRITE_ERROR;
else if (status != B_OK)
return status;
*trimmedBlocks += trimLength;
length -= trimLength;
lba += trimLength;
}
}
return status;
}
status_t
periph_trim_device(scsi_periph_device_info* device, scsi_ccb* request,
scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
{
*trimmedBlocks = 0;
if (device->unmap_command == TRIM_NONE
|| device->max_unmap_lba_count == 0
|| device->max_unmap_descriptor_count == 0)
return B_UNSUPPORTED;
switch (device->unmap_command) {
case TRIM_UNMAP:
return trim_unmap(device, request, ranges, rangeCount,
trimmedBlocks);
case TRIM_WRITESAME16:
return trim_writesame16(device, request, ranges, rangeCount,
trimmedBlocks);
case TRIM_WRITESAME10:
return trim_writesame10(device, request, ranges, rangeCount,
trimmedBlocks);
default:
return B_UNSUPPORTED;
}
}