124593e2cSAxel Dörfler /*
28b1d35bdSDavid Sebek * Copyright 2021 David Sebek, dasebek@gmail.com
38b1d35bdSDavid Sebek * Copyright 2004-2013 Haiku, Inc.
48b1d35bdSDavid Sebek * Copyright 2002-2003 Thomas Kurschel
58b1d35bdSDavid Sebek * All rights reserved. Distributed under the terms of the MIT License.
624593e2cSAxel Dörfler */
724593e2cSAxel Dörfler
8a1e8da41SAxel Dörfler
9960c56aeSAxel Dörfler //! Handling of block device
1024593e2cSAxel Dörfler
1124593e2cSAxel Dörfler
1224593e2cSAxel Dörfler #include <string.h>
1324593e2cSAxel Dörfler
1499086aa3SAxel Dörfler #include <AutoDeleter.h>
1599086aa3SAxel Dörfler
1699086aa3SAxel Dörfler #include "scsi_periph_int.h"
1799086aa3SAxel Dörfler
1824593e2cSAxel Dörfler
198b1d35bdSDavid Sebek // UNMAP command limits
208b1d35bdSDavid Sebek #define UNMAP_MAX_LBA_VALUE UINT64_MAX
218b1d35bdSDavid Sebek #define UNMAP_MAX_BLOCK_COUNT_VALUE UINT32_MAX
228b1d35bdSDavid Sebek #define UNMAP_MAX_DESCRIPTORS 4095
238b1d35bdSDavid Sebek // Limit imposed by the UNMAP command structure
248b1d35bdSDavid Sebek #define UNMAP_DEFAULT_DESCRIPTORS 255
258b1d35bdSDavid Sebek // Reasonable default (?) when not specified by the device
268b1d35bdSDavid Sebek
278b1d35bdSDavid Sebek // WRITE SAME (16) command limits
288b1d35bdSDavid Sebek #define WS16_MAX_LBA_VALUE UINT64_MAX
298b1d35bdSDavid Sebek #define WS16_MAX_BLOCK_COUNT_VALUE UINT32_MAX
308b1d35bdSDavid Sebek
318b1d35bdSDavid Sebek // WRITE SAME (10) command limits
328b1d35bdSDavid Sebek #define WS10_MAX_LBA_VALUE UINT32_MAX
338b1d35bdSDavid Sebek #define WS10_MAX_BLOCK_COUNT_VALUE UINT16_MAX
348b1d35bdSDavid Sebek
358b1d35bdSDavid Sebek
368b1d35bdSDavid Sebek struct CapacityInfo {
378b1d35bdSDavid Sebek // Result of the READ CAPACITY command
388b1d35bdSDavid Sebek bool capacityFilled;
398b1d35bdSDavid Sebek uint64 lastLba;
408b1d35bdSDavid Sebek uint32 blockSize;
41*688acf41SJérôme Duval uint32 physicalBlockSize;
428b1d35bdSDavid Sebek
43*688acf41SJérôme Duval // Provisioning info from READ CAPACITY
448b1d35bdSDavid Sebek bool provisioningFilled;
458b1d35bdSDavid Sebek bool lbpme;
468b1d35bdSDavid Sebek bool lbprz;
478b1d35bdSDavid Sebek };
488b1d35bdSDavid Sebek
498b1d35bdSDavid Sebek
508b1d35bdSDavid Sebek struct UnmapSupport {
518b1d35bdSDavid Sebek // UNMAP commands supported by the device
528b1d35bdSDavid Sebek bool commandSupportFilled;
538b1d35bdSDavid Sebek bool unmapSupported;
548b1d35bdSDavid Sebek bool ws16Supported;
558b1d35bdSDavid Sebek bool ws10Supported;
568b1d35bdSDavid Sebek
578b1d35bdSDavid Sebek // Block limits for UNMAP commands
588b1d35bdSDavid Sebek bool blockLimitsFilled;
598b1d35bdSDavid Sebek uint32 maxUnmapLbaCount;
608b1d35bdSDavid Sebek uint32 maxUnmapDescriptorCount;
618b1d35bdSDavid Sebek uint64 maxWritesameLength;
628b1d35bdSDavid Sebek };
638b1d35bdSDavid Sebek
648b1d35bdSDavid Sebek
658b1d35bdSDavid Sebek static bool
prefer_read_capacity_16(scsi_periph_device_info * device)668b1d35bdSDavid Sebek prefer_read_capacity_16(scsi_periph_device_info* device)
678b1d35bdSDavid Sebek {
688b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL;
698b1d35bdSDavid Sebek size_t inquiryDataLength;
708b1d35bdSDavid Sebek
718b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
728b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK
738b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) {
748b1d35bdSDavid Sebek return false;
758b1d35bdSDavid Sebek }
768b1d35bdSDavid Sebek
778b1d35bdSDavid Sebek if (inquiryData->protect)
788b1d35bdSDavid Sebek return true;
798b1d35bdSDavid Sebek
808b1d35bdSDavid Sebek if (inquiryData->ansi_version > 0x04 /* SPC-2 */)
818b1d35bdSDavid Sebek return true;
828b1d35bdSDavid Sebek
838b1d35bdSDavid Sebek return false;
848b1d35bdSDavid Sebek }
858b1d35bdSDavid Sebek
868b1d35bdSDavid Sebek
878b1d35bdSDavid Sebek static bool
vpd_pages_supported(scsi_periph_device_info * device)888b1d35bdSDavid Sebek vpd_pages_supported(scsi_periph_device_info* device)
898b1d35bdSDavid Sebek {
908b1d35bdSDavid Sebek const scsi_res_inquiry* inquiryData = NULL;
918b1d35bdSDavid Sebek size_t inquiryDataLength;
928b1d35bdSDavid Sebek
938b1d35bdSDavid Sebek if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
948b1d35bdSDavid Sebek (const void**)&inquiryData, &inquiryDataLength, true) != B_OK
958b1d35bdSDavid Sebek || inquiryDataLength != sizeof(*inquiryData)) {
968b1d35bdSDavid Sebek return false;
978b1d35bdSDavid Sebek }
988b1d35bdSDavid Sebek
998b1d35bdSDavid Sebek if (inquiryData->ansi_version >= 0x04 /* SPC-2 */)
1008b1d35bdSDavid Sebek return true;
1018b1d35bdSDavid Sebek
1028b1d35bdSDavid Sebek return false;
1038b1d35bdSDavid Sebek }
1048b1d35bdSDavid Sebek
1058b1d35bdSDavid Sebek
1068b1d35bdSDavid Sebek static status_t
read_capacity_10(scsi_periph_device_info * device,scsi_ccb * request,CapacityInfo * capacityInfo)1078b1d35bdSDavid Sebek read_capacity_10(scsi_periph_device_info* device, scsi_ccb* request,
1088b1d35bdSDavid Sebek CapacityInfo* capacityInfo)
1098b1d35bdSDavid Sebek {
1108b1d35bdSDavid Sebek capacityInfo->capacityFilled = false;
1118b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false;
1128b1d35bdSDavid Sebek
1138b1d35bdSDavid Sebek scsi_res_read_capacity capacityResult;
1148b1d35bdSDavid Sebek memset(&capacityResult, 0, sizeof(capacityResult));
1158b1d35bdSDavid Sebek
1168b1d35bdSDavid Sebek scsi_cmd_read_capacity* cmd = (scsi_cmd_read_capacity*)request->cdb;
1178b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd));
1188b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_READ_CAPACITY;
1198b1d35bdSDavid Sebek // we don't set PMI (partial medium indicator) as we want the whole capacity;
1208b1d35bdSDavid Sebek // in this case, all other parameters must be zero
1218b1d35bdSDavid Sebek
1228b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN;
1238b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd);
1248b1d35bdSDavid Sebek request->sort = -1;
1258b1d35bdSDavid Sebek request->timeout = device->std_timeout;
1268b1d35bdSDavid Sebek
1278b1d35bdSDavid Sebek request->data = (uint8*)&capacityResult;
1288b1d35bdSDavid Sebek request->data_length = sizeof(capacityResult);
1298b1d35bdSDavid Sebek request->sg_list = NULL;
1308b1d35bdSDavid Sebek
1318b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request);
1328b1d35bdSDavid Sebek
1338b1d35bdSDavid Sebek if (res == B_OK && request->data_resid == 0) {
1348b1d35bdSDavid Sebek capacityInfo->capacityFilled = true;
1358b1d35bdSDavid Sebek capacityInfo->lastLba
1368b1d35bdSDavid Sebek = (uint32)B_BENDIAN_TO_HOST_INT32(capacityResult.lba);
1378b1d35bdSDavid Sebek capacityInfo->blockSize
1388b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityResult.block_size);
139*688acf41SJérôme Duval capacityInfo->physicalBlockSize = capacityInfo->blockSize;
1408b1d35bdSDavid Sebek }
1418b1d35bdSDavid Sebek
1428b1d35bdSDavid Sebek return res;
1438b1d35bdSDavid Sebek }
1448b1d35bdSDavid Sebek
1458b1d35bdSDavid Sebek
1468b1d35bdSDavid Sebek static status_t
read_capacity_16(scsi_periph_device_info * device,scsi_ccb * request,CapacityInfo * capacityInfo)1478b1d35bdSDavid Sebek read_capacity_16(scsi_periph_device_info* device, scsi_ccb* request,
1488b1d35bdSDavid Sebek CapacityInfo* capacityInfo)
1498b1d35bdSDavid Sebek {
1508b1d35bdSDavid Sebek capacityInfo->capacityFilled = false;
1518b1d35bdSDavid Sebek capacityInfo->provisioningFilled = false;
1528b1d35bdSDavid Sebek
1538b1d35bdSDavid Sebek scsi_res_read_capacity_long capacityLongResult;
1548b1d35bdSDavid Sebek memset(&capacityLongResult, 0, sizeof(capacityLongResult));
1558b1d35bdSDavid Sebek
1568b1d35bdSDavid Sebek scsi_cmd_read_capacity_long* cmd
1578b1d35bdSDavid Sebek = (scsi_cmd_read_capacity_long*)request->cdb;
1588b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd));
1598b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_SERVICE_ACTION_IN;
1608b1d35bdSDavid Sebek cmd->service_action = SCSI_SAI_READ_CAPACITY_16;
1618b1d35bdSDavid Sebek cmd->alloc_length = B_HOST_TO_BENDIAN_INT32(sizeof(capacityLongResult));
1628b1d35bdSDavid Sebek
1638b1d35bdSDavid Sebek request->flags = SCSI_DIR_IN;
1648b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd);
1658b1d35bdSDavid Sebek request->sort = -1;
1668b1d35bdSDavid Sebek request->timeout = device->std_timeout;
1678b1d35bdSDavid Sebek
1688b1d35bdSDavid Sebek request->data = (uint8*)&capacityLongResult;
1698b1d35bdSDavid Sebek request->data_length = sizeof(capacityLongResult);
1708b1d35bdSDavid Sebek request->sg_list = NULL;
1718b1d35bdSDavid Sebek
1728b1d35bdSDavid Sebek status_t res = periph_safe_exec(device, request);
1738b1d35bdSDavid Sebek
1748b1d35bdSDavid Sebek if (res == B_OK && request->data_resid
1758b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 12) {
1768b1d35bdSDavid Sebek // At least the last LBA and sector size have been transfered
1778b1d35bdSDavid Sebek capacityInfo->capacityFilled = true;
1788b1d35bdSDavid Sebek capacityInfo->lastLba
1798b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba);
1808b1d35bdSDavid Sebek capacityInfo->blockSize
1818b1d35bdSDavid Sebek = B_BENDIAN_TO_HOST_INT32(capacityLongResult.block_size);
182*688acf41SJérôme Duval capacityInfo->physicalBlockSize = capacityInfo->blockSize
183*688acf41SJérôme Duval * (1 << capacityLongResult.logical_blocks_per_physical_block_exponent);
1848b1d35bdSDavid Sebek }
1858b1d35bdSDavid Sebek
1868b1d35bdSDavid Sebek if (res == B_OK && request->data_resid
1878b1d35bdSDavid Sebek <= (int32)sizeof(scsi_res_read_capacity_long) - 15) {
1888b1d35bdSDavid Sebek // lbpme and lbprz bits were received too
1898b1d35bdSDavid Sebek capacityInfo->provisioningFilled = true;
1908b1d35bdSDavid Sebek capacityInfo->lbpme = capacityLongResult.lbpme;
1918b1d35bdSDavid Sebek capacityInfo->lbprz = capacityLongResult.lbprz;
1928b1d35bdSDavid Sebek }
1938b1d35bdSDavid Sebek
1948b1d35bdSDavid Sebek return res;
1958b1d35bdSDavid Sebek }
1968b1d35bdSDavid Sebek
1978b1d35bdSDavid Sebek
1988b1d35bdSDavid Sebek static status_t
get_unmap_commands(scsi_periph_device_info * device,scsi_ccb * request,UnmapSupport * unmapSupport)1998b1d35bdSDavid Sebek get_unmap_commands(scsi_periph_device_info* device, scsi_ccb* request,
2008b1d35bdSDavid Sebek UnmapSupport* unmapSupport)
2018b1d35bdSDavid Sebek {
2028b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = false;
2038b1d35bdSDavid Sebek
2048b1d35bdSDavid Sebek scsi_page_lb_provisioning vpdProvisioning;
2058b1d35bdSDavid Sebek memset(&vpdProvisioning, 0, sizeof(vpdProvisioning));
2068b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request,
2078b1d35bdSDavid Sebek SCSI_PAGE_LB_PROVISIONING, &vpdProvisioning, sizeof(vpdProvisioning));
2088b1d35bdSDavid Sebek
2098b1d35bdSDavid Sebek if (vpdStatus == B_OK
2108b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_lb_provisioning) - 6
2118b1d35bdSDavid Sebek && vpdProvisioning.page_code == SCSI_PAGE_LB_PROVISIONING
2128b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdProvisioning.page_length) >= 2) {
2138b1d35bdSDavid Sebek unmapSupport->commandSupportFilled = true;
2148b1d35bdSDavid Sebek unmapSupport->unmapSupported = vpdProvisioning.lbpu;
2158b1d35bdSDavid Sebek unmapSupport->ws16Supported = vpdProvisioning.lbpws;
2168b1d35bdSDavid Sebek unmapSupport->ws10Supported = vpdProvisioning.lbpws10;
2178b1d35bdSDavid Sebek }
2188b1d35bdSDavid Sebek
2198b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE)
2208b1d35bdSDavid Sebek return B_ERROR;
2218b1d35bdSDavid Sebek
2228b1d35bdSDavid Sebek return vpdStatus;
2238b1d35bdSDavid Sebek }
2248b1d35bdSDavid Sebek
2258b1d35bdSDavid Sebek
2268b1d35bdSDavid Sebek static status_t
get_unmap_limits(scsi_periph_device_info * device,scsi_ccb * request,UnmapSupport * unmapSupport)2278b1d35bdSDavid Sebek get_unmap_limits(scsi_periph_device_info* device, scsi_ccb* request,
2288b1d35bdSDavid Sebek UnmapSupport* unmapSupport)
2298b1d35bdSDavid Sebek {
2308b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = false;
2318b1d35bdSDavid Sebek
2328b1d35bdSDavid Sebek scsi_page_block_limits vpdBlockLimits;
2338b1d35bdSDavid Sebek memset(&vpdBlockLimits, 0, sizeof(vpdBlockLimits));
2348b1d35bdSDavid Sebek status_t vpdStatus = vpd_page_get(device, request,
2358b1d35bdSDavid Sebek SCSI_PAGE_BLOCK_LIMITS, &vpdBlockLimits, sizeof(vpdBlockLimits));
2368b1d35bdSDavid Sebek
2378b1d35bdSDavid Sebek if (vpdStatus == B_OK
2388b1d35bdSDavid Sebek && request->data_resid <= (int32)sizeof(scsi_page_block_limits) - 44
2398b1d35bdSDavid Sebek && vpdBlockLimits.page_code == SCSI_PAGE_BLOCK_LIMITS
2408b1d35bdSDavid Sebek && B_BENDIAN_TO_HOST_INT16(vpdBlockLimits.page_length) == 0x3c) {
2418b1d35bdSDavid Sebek unmapSupport->blockLimitsFilled = true;
2428b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount = B_BENDIAN_TO_HOST_INT32(
2438b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_lba_count);
2448b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount = B_BENDIAN_TO_HOST_INT32(
2458b1d35bdSDavid Sebek vpdBlockLimits.max_unmap_blk_count);
2468b1d35bdSDavid Sebek unmapSupport->maxWritesameLength = B_BENDIAN_TO_HOST_INT64(
2478b1d35bdSDavid Sebek vpdBlockLimits.max_write_same_length);
2488b1d35bdSDavid Sebek }
2498b1d35bdSDavid Sebek
2508b1d35bdSDavid Sebek if (vpdStatus == B_BAD_VALUE)
2518b1d35bdSDavid Sebek return B_ERROR;
2528b1d35bdSDavid Sebek
2538b1d35bdSDavid Sebek return vpdStatus;
2548b1d35bdSDavid Sebek }
2558b1d35bdSDavid Sebek
2568b1d35bdSDavid Sebek
2578b1d35bdSDavid Sebek static void
determine_unmap_support(const UnmapSupport * unmapSupport,enum trim_command * unmapCommand,uint32 * maxLbaCount,uint32 * maxDescriptorCount)2588b1d35bdSDavid Sebek determine_unmap_support(const UnmapSupport* unmapSupport,
2598b1d35bdSDavid Sebek enum trim_command* unmapCommand, uint32* maxLbaCount,
2608b1d35bdSDavid Sebek uint32* maxDescriptorCount)
2618b1d35bdSDavid Sebek {
2628b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
2638b1d35bdSDavid Sebek if (unmapSupport->commandSupportFilled)
2648b1d35bdSDavid Sebek dprintf("TRIM: device reports (LBP VPD): LBPU = %d, LBPWS = %d,"
2658b1d35bdSDavid Sebek " LBPWS10 = %d\n", unmapSupport->unmapSupported,
2668b1d35bdSDavid Sebek unmapSupport->ws16Supported, unmapSupport->ws10Supported);
2678b1d35bdSDavid Sebek else
2688b1d35bdSDavid Sebek dprintf("TRIM: could not get the LBP VPD of the device\n");
2698b1d35bdSDavid Sebek if (unmapSupport->blockLimitsFilled)
2708b1d35bdSDavid Sebek dprintf("TRIM: device reports (Block Limits VPD):"
2718b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP LBA COUNT = %" B_PRIu32
2728b1d35bdSDavid Sebek "\nTRIM: MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT = %" B_PRIu32
2738b1d35bdSDavid Sebek "\nTRIM: MAXIMUM WRITESAME LENGTH = %" B_PRIu64 "\n",
2748b1d35bdSDavid Sebek unmapSupport->maxUnmapLbaCount,
2758b1d35bdSDavid Sebek unmapSupport->maxUnmapDescriptorCount,
2768b1d35bdSDavid Sebek unmapSupport->maxWritesameLength);
2778b1d35bdSDavid Sebek else
2788b1d35bdSDavid Sebek dprintf("TRIM: could not get Block Limits VPD of the device\n");
2798b1d35bdSDavid Sebek #endif
2808b1d35bdSDavid Sebek
2818b1d35bdSDavid Sebek *unmapCommand = TRIM_NONE;
2828b1d35bdSDavid Sebek *maxLbaCount = 0;
2838b1d35bdSDavid Sebek *maxDescriptorCount = 0;
2848b1d35bdSDavid Sebek
2858b1d35bdSDavid Sebek if (!unmapSupport->commandSupportFilled
2868b1d35bdSDavid Sebek || !unmapSupport->blockLimitsFilled)
2878b1d35bdSDavid Sebek return;
2888b1d35bdSDavid Sebek
2898b1d35bdSDavid Sebek if (unmapSupport->unmapSupported
2908b1d35bdSDavid Sebek && unmapSupport->maxUnmapLbaCount > 0
2918b1d35bdSDavid Sebek && unmapSupport->maxUnmapDescriptorCount > 0) {
2928b1d35bdSDavid Sebek *unmapCommand = TRIM_UNMAP;
2938b1d35bdSDavid Sebek *maxLbaCount = unmapSupport->maxUnmapLbaCount;
2948b1d35bdSDavid Sebek if (unmapSupport->maxUnmapDescriptorCount == UINT32_MAX
2958b1d35bdSDavid Sebek || unmapSupport->maxUnmapDescriptorCount > UNMAP_MAX_DESCRIPTORS) {
2968b1d35bdSDavid Sebek // Choose a reasonable value instead
2978b1d35bdSDavid Sebek *maxDescriptorCount = UNMAP_DEFAULT_DESCRIPTORS;
2988b1d35bdSDavid Sebek } else {
2998b1d35bdSDavid Sebek *maxDescriptorCount = unmapSupport->maxUnmapDescriptorCount;
3008b1d35bdSDavid Sebek }
3018b1d35bdSDavid Sebek }
3028b1d35bdSDavid Sebek
3038b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws16Supported) {
3048b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength;
3058b1d35bdSDavid Sebek if (maxLength == 0) {
3068b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead
3078b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0)
3088b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount;
3098b1d35bdSDavid Sebek else
3108b1d35bdSDavid Sebek maxLength = WS16_MAX_BLOCK_COUNT_VALUE;
3118b1d35bdSDavid Sebek }
3128b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME16;
3138b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS16_MAX_BLOCK_COUNT_VALUE);
3148b1d35bdSDavid Sebek *maxDescriptorCount = 1;
3158b1d35bdSDavid Sebek }
3168b1d35bdSDavid Sebek
3178b1d35bdSDavid Sebek if (*unmapCommand == TRIM_NONE && unmapSupport->ws10Supported) {
3188b1d35bdSDavid Sebek uint64 maxLength = unmapSupport->maxWritesameLength;
3198b1d35bdSDavid Sebek if (maxLength == 0) {
3208b1d35bdSDavid Sebek // WRITE SAME limit not reported, try UNMAP limit instead
3218b1d35bdSDavid Sebek if (unmapSupport->maxUnmapLbaCount > 0)
3228b1d35bdSDavid Sebek maxLength = unmapSupport->maxUnmapLbaCount;
3238b1d35bdSDavid Sebek else
3248b1d35bdSDavid Sebek maxLength = WS10_MAX_BLOCK_COUNT_VALUE;
3258b1d35bdSDavid Sebek }
3268b1d35bdSDavid Sebek *unmapCommand = TRIM_WRITESAME10;
3278b1d35bdSDavid Sebek *maxLbaCount = min_c(maxLength, WS10_MAX_BLOCK_COUNT_VALUE);
3288b1d35bdSDavid Sebek *maxDescriptorCount = 1;
3298b1d35bdSDavid Sebek }
3308b1d35bdSDavid Sebek }
3318b1d35bdSDavid Sebek
3328b1d35bdSDavid Sebek
33324593e2cSAxel Dörfler status_t
periph_check_capacity(scsi_periph_device_info * device,scsi_ccb * request)33424593e2cSAxel Dörfler periph_check_capacity(scsi_periph_device_info* device, scsi_ccb* request)
33524593e2cSAxel Dörfler {
3368b1d35bdSDavid Sebek CapacityInfo capacityInfo = {0};
33724593e2cSAxel Dörfler status_t res;
33824593e2cSAxel Dörfler
33924593e2cSAxel Dörfler SHOW_FLOW(3, "%p, %p", device, request);
34024593e2cSAxel Dörfler
34124593e2cSAxel Dörfler // driver doesn't support capacity callback - seems to be no block
34224593e2cSAxel Dörfler // device driver, so ignore
34324593e2cSAxel Dörfler if (device->callbacks->set_capacity == NULL)
34424593e2cSAxel Dörfler return B_OK;
34524593e2cSAxel Dörfler
3468b1d35bdSDavid Sebek if (prefer_read_capacity_16(device)) {
3478b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 tried first");
3488b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo);
34924593e2cSAxel Dörfler
3508b1d35bdSDavid Sebek if (res == B_ERROR) {
3518b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 16 failed, trying READ CAPACITY 10");
3528b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo);
3538b1d35bdSDavid Sebek }
3548b1d35bdSDavid Sebek } else {
3558b1d35bdSDavid Sebek SHOW_FLOW0(3, "READ CAPACITY 10 tried first");
3568b1d35bdSDavid Sebek res = read_capacity_10(device, request, &capacityInfo);
35724593e2cSAxel Dörfler
3588b1d35bdSDavid Sebek if (res == B_OK && capacityInfo.capacityFilled
3598b1d35bdSDavid Sebek && capacityInfo.lastLba == UINT32_MAX) {
3608b1d35bdSDavid Sebek SHOW_FLOW0(3, "Device is too large, trying READ CAPACITY 16");
3618b1d35bdSDavid Sebek res = read_capacity_16(device, request, &capacityInfo);
3628b1d35bdSDavid Sebek }
3638b1d35bdSDavid Sebek }
36424593e2cSAxel Dörfler
3658b1d35bdSDavid Sebek uint64 capacity;
366*688acf41SJérôme Duval uint32 blockSize, physicalBlockSize;
3678b1d35bdSDavid Sebek
3688b1d35bdSDavid Sebek if (capacityInfo.capacityFilled) {
3698b1d35bdSDavid Sebek capacity = capacityInfo.lastLba + 1;
3708b1d35bdSDavid Sebek blockSize = capacityInfo.blockSize;
371*688acf41SJérôme Duval physicalBlockSize = capacityInfo.physicalBlockSize;
3728b1d35bdSDavid Sebek } else {
3738b1d35bdSDavid Sebek capacity = 0;
3748b1d35bdSDavid Sebek blockSize = 0;
375*688acf41SJérôme Duval physicalBlockSize = 0;
3768b1d35bdSDavid Sebek }
3778b1d35bdSDavid Sebek
3788b1d35bdSDavid Sebek enum trim_command unmapCommand = TRIM_NONE;
3798b1d35bdSDavid Sebek uint32 maxLbaCount = 0;
3808b1d35bdSDavid Sebek uint32 maxDescriptorCount = 0;
3818b1d35bdSDavid Sebek
3828b1d35bdSDavid Sebek if (capacityInfo.provisioningFilled
3838b1d35bdSDavid Sebek && capacityInfo.lbpme
3848b1d35bdSDavid Sebek && vpd_pages_supported(device)) {
3858b1d35bdSDavid Sebek UnmapSupport unmapSupport = {0};
3868b1d35bdSDavid Sebek
3878b1d35bdSDavid Sebek // Don't fail if the device doesn't support the command
3888b1d35bdSDavid Sebek // but fail if some other error happens
3898b1d35bdSDavid Sebek if (res == B_OK) {
3908b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_commands(device, request,
3918b1d35bdSDavid Sebek &unmapSupport);
3928b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR)
3938b1d35bdSDavid Sebek res = vpdStatus;
3948b1d35bdSDavid Sebek }
3958b1d35bdSDavid Sebek
3968b1d35bdSDavid Sebek if (res == B_OK) {
3978b1d35bdSDavid Sebek status_t vpdStatus = get_unmap_limits(device, request,
3988b1d35bdSDavid Sebek &unmapSupport);
3998b1d35bdSDavid Sebek if (vpdStatus != B_OK && vpdStatus != B_ERROR)
4008b1d35bdSDavid Sebek res = vpdStatus;
4018b1d35bdSDavid Sebek }
4028b1d35bdSDavid Sebek
4038b1d35bdSDavid Sebek determine_unmap_support(&unmapSupport, &unmapCommand,
4048b1d35bdSDavid Sebek &maxLbaCount, &maxDescriptorCount);
4058b1d35bdSDavid Sebek
4068b1d35bdSDavid Sebek if (maxLbaCount == 0 || maxDescriptorCount == 0)
4078b1d35bdSDavid Sebek unmapCommand = TRIM_NONE;
4088b1d35bdSDavid Sebek }
40924593e2cSAxel Dörfler
41024593e2cSAxel Dörfler if (res == B_DEV_MEDIA_CHANGED) {
41124593e2cSAxel Dörfler // in this case, the error handler has already called check_capacity
41224593e2cSAxel Dörfler // recursively, so we ignore our (invalid) result
41324593e2cSAxel Dörfler SHOW_FLOW0(3, "ignore result because medium change");
41424593e2cSAxel Dörfler return B_DEV_MEDIA_CHANGED;
41524593e2cSAxel Dörfler }
41624593e2cSAxel Dörfler
4178b1d35bdSDavid Sebek if (res == B_OK && !capacityInfo.capacityFilled)
4188b1d35bdSDavid Sebek // Although the capacity and block size will be set to 0 in this case,
4198b1d35bdSDavid Sebek // it is also better to inform the caller that these values were not
4208b1d35bdSDavid Sebek // reported by the device
4218b1d35bdSDavid Sebek res = B_ERROR;
42224593e2cSAxel Dörfler
4238b1d35bdSDavid Sebek SHOW_FLOW(3, "capacity = %" B_PRIu64 ", block_size = %" B_PRIu32
4248b1d35bdSDavid Sebek " (%sreported)", capacity, blockSize,
4258b1d35bdSDavid Sebek capacityInfo.capacityFilled ? "" : "not ");
4268b1d35bdSDavid Sebek SHOW_INFO(1, "TRIM: Setting trim support to %s",
4278b1d35bdSDavid Sebek unmapCommand == TRIM_NONE ? "disabled"
4288b1d35bdSDavid Sebek : unmapCommand == TRIM_UNMAP ? "UNMAP"
4298b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME16 ? "WRITE SAME (16)"
4308b1d35bdSDavid Sebek : unmapCommand == TRIM_WRITESAME10 ? "WRITE SAME (10)"
4318b1d35bdSDavid Sebek : "unknown");
4328b1d35bdSDavid Sebek SHOW_FLOW(3, "TRIM: Block limits: size = %" B_PRIu32
4338b1d35bdSDavid Sebek ", descriptors = %" B_PRIu32, maxLbaCount, maxDescriptorCount);
434f10a55a6SJérôme Duval
435a1e8da41SAxel Dörfler mutex_lock(&device->mutex);
4368b1d35bdSDavid Sebek // Was there a reason why this mutex
4378b1d35bdSDavid Sebek // was previously locked much earlier?
438f10a55a6SJérôme Duval
4398b1d35bdSDavid Sebek device->unmap_command = unmapCommand;
4408b1d35bdSDavid Sebek device->max_unmap_lba_count = maxLbaCount;
4418b1d35bdSDavid Sebek device->max_unmap_descriptor_count = maxDescriptorCount;
44224593e2cSAxel Dörfler
44324593e2cSAxel Dörfler device->block_size = blockSize;
444*688acf41SJérôme Duval device->physical_block_size = physicalBlockSize;
44524593e2cSAxel Dörfler
44624593e2cSAxel Dörfler device->callbacks->set_capacity(device->periph_device,
447*688acf41SJérôme Duval capacity, blockSize, physicalBlockSize);
44824593e2cSAxel Dörfler
44924593e2cSAxel Dörfler /* device->byte2blk_shift = log2( device->block_size );
45024593e2cSAxel Dörfler if( device->byte2blk_shift < 0 ) {
45124593e2cSAxel Dörfler // this may be too restrictive...
45224593e2cSAxel Dörfler device->capacity = -1;
45324593e2cSAxel Dörfler return ERR_DEV_GENERAL;
45424593e2cSAxel Dörfler }*/
45524593e2cSAxel Dörfler
456a1e8da41SAxel Dörfler mutex_unlock(&device->mutex);
45724593e2cSAxel Dörfler
45824593e2cSAxel Dörfler SHOW_FLOW(3, "done (%s)", strerror(res));
45924593e2cSAxel Dörfler
46024593e2cSAxel Dörfler return res;
46124593e2cSAxel Dörfler }
46224593e2cSAxel Dörfler
463960c56aeSAxel Dörfler
4648b1d35bdSDavid Sebek static status_t
trim_unmap(scsi_periph_device_info * device,scsi_ccb * request,scsi_block_range * ranges,uint32 rangeCount,uint64 * trimmedBlocks)4658b1d35bdSDavid Sebek trim_unmap(scsi_periph_device_info* device, scsi_ccb* request,
4668b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
467960c56aeSAxel Dörfler {
4688b1d35bdSDavid Sebek uint64 maxLength = UNMAP_MAX_BLOCK_COUNT_VALUE;
4698b1d35bdSDavid Sebek uint64 maxBlocksInRequest = device->max_unmap_lba_count;
4708b1d35bdSDavid Sebek uint32 maxDescriptors = device->max_unmap_descriptor_count;
4718b1d35bdSDavid Sebek
4728b1d35bdSDavid Sebek *trimmedBlocks = 0;
4738b1d35bdSDavid Sebek
4748b1d35bdSDavid Sebek // Allocate a single buffer and re-use it between requests
4758b1d35bdSDavid Sebek size_t expectedDescriptorCount = 0;
4768b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) {
4778b1d35bdSDavid Sebek expectedDescriptorCount += ranges[i].size / maxLength;
4788b1d35bdSDavid Sebek if (ranges[i].size % maxLength != 0)
4798b1d35bdSDavid Sebek expectedDescriptorCount++;
4808b1d35bdSDavid Sebek }
4818b1d35bdSDavid Sebek expectedDescriptorCount = min_c(expectedDescriptorCount, maxDescriptors);
4828b1d35bdSDavid Sebek
4838b1d35bdSDavid Sebek size_t unmapListAllocatedSize = (expectedDescriptorCount - 1)
48499086aa3SAxel Dörfler * sizeof(scsi_unmap_block_descriptor)
48599086aa3SAxel Dörfler + sizeof(scsi_unmap_parameter_list);
48699086aa3SAxel Dörfler
4878b1d35bdSDavid Sebek scsi_unmap_parameter_list* unmapList
4888b1d35bdSDavid Sebek = (scsi_unmap_parameter_list*)malloc(unmapListAllocatedSize);
4898b1d35bdSDavid Sebek if (unmapList == NULL)
49099086aa3SAxel Dörfler return B_NO_MEMORY;
49199086aa3SAxel Dörfler
4928b1d35bdSDavid Sebek MemoryDeleter deleter(unmapList);
49399086aa3SAxel Dörfler
4948b1d35bdSDavid Sebek status_t status = B_OK;
4958b1d35bdSDavid Sebek uint32 descriptorIndex = 0;
4968b1d35bdSDavid Sebek uint64 trimmedBlocksInRequest = 0;
4978b1d35bdSDavid Sebek memset(unmapList, 0, unmapListAllocatedSize);
49899086aa3SAxel Dörfler for (uint32 i = 0; i < rangeCount; i++) {
4998b1d35bdSDavid Sebek uint64 lba = ranges[i].lba;
5008b1d35bdSDavid Sebek uint64 length = ranges[i].size;
5018b1d35bdSDavid Sebek
5028b1d35bdSDavid Sebek if (length == 0)
5038b1d35bdSDavid Sebek continue; // Length of 0 would be ignored by the device anyway
5048b1d35bdSDavid Sebek
5058b1d35bdSDavid Sebek if (lba > UNMAP_MAX_LBA_VALUE) {
5068b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!"
5078b1d35bdSDavid Sebek " This unmap range will be skipped.");
5088b1d35bdSDavid Sebek continue;
50999086aa3SAxel Dörfler }
51099086aa3SAxel Dörfler
5118b1d35bdSDavid Sebek // Split large ranges if needed.
5128b1d35bdSDavid Sebek // Range length is limited by:
5138b1d35bdSDavid Sebek // - the UNMAP_MAX_BLOCK_COUNT_VALUE constant
5148b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by
5158b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page
5168b1d35bdSDavid Sebek while (length > 0) {
5178b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength);
5188b1d35bdSDavid Sebek trimLength = min_c(trimLength,
5198b1d35bdSDavid Sebek maxBlocksInRequest - trimmedBlocksInRequest);
5208b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].lba
5218b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT64(lba);
5228b1d35bdSDavid Sebek unmapList->blocks[descriptorIndex].block_count
5238b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT32(trimLength);
5248b1d35bdSDavid Sebek descriptorIndex++;
5258b1d35bdSDavid Sebek trimmedBlocksInRequest += trimLength;
5268b1d35bdSDavid Sebek
5278b1d35bdSDavid Sebek // Split into multiple requests if needed.
5288b1d35bdSDavid Sebek // The number of UNMAP block descriptors is limited by:
5298b1d35bdSDavid Sebek // - the number of block descriptors cannot exceed the
5308b1d35bdSDavid Sebek // MAXIMUM UNMAP PARAMETER COUNT value in the Block Limits VPD
5318b1d35bdSDavid Sebek // - the size of our buffer
5328b1d35bdSDavid Sebek // - what fits in one UNMAP command
5338b1d35bdSDavid Sebek // - the total number of LBAs in one UNMAP command is limited by
5348b1d35bdSDavid Sebek // the MAX UNMAP LBA COUNT field in the Block Limits VPD page
5358b1d35bdSDavid Sebek if (descriptorIndex >= maxDescriptors
5368b1d35bdSDavid Sebek || descriptorIndex >= expectedDescriptorCount
5378b1d35bdSDavid Sebek || descriptorIndex >= UNMAP_MAX_DESCRIPTORS
5388b1d35bdSDavid Sebek || trimmedBlocksInRequest >= maxBlocksInRequest
5398b1d35bdSDavid Sebek || (i == rangeCount - 1 && length <= maxLength))
5408b1d35bdSDavid Sebek {
5418b1d35bdSDavid Sebek uint16 unmapListSize = (descriptorIndex - 1)
5428b1d35bdSDavid Sebek * sizeof(scsi_unmap_block_descriptor)
5438b1d35bdSDavid Sebek + sizeof(scsi_unmap_parameter_list);
5448b1d35bdSDavid Sebek unmapList->data_length = B_HOST_TO_BENDIAN_INT16(unmapListSize
5458b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, block_data_length));
5468b1d35bdSDavid Sebek unmapList->block_data_length
5478b1d35bdSDavid Sebek = B_HOST_TO_BENDIAN_INT16(unmapListSize
5488b1d35bdSDavid Sebek - offsetof(scsi_unmap_parameter_list, blocks));
549960c56aeSAxel Dörfler
55099086aa3SAxel Dörfler scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb;
551960c56aeSAxel Dörfler memset(cmd, 0, sizeof(*cmd));
55299086aa3SAxel Dörfler cmd->opcode = SCSI_OP_UNMAP;
5538b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(unmapListSize);
55499086aa3SAxel Dörfler
5558b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT;
556960c56aeSAxel Dörfler request->cdb_length = sizeof(*cmd);
5578b1d35bdSDavid Sebek request->sort = B_BENDIAN_TO_HOST_INT64(
5588b1d35bdSDavid Sebek unmapList->blocks[0].lba);
5598b1d35bdSDavid Sebek request->timeout = device->std_timeout;
560960c56aeSAxel Dörfler
5618b1d35bdSDavid Sebek request->data = (uint8*)unmapList;
5628b1d35bdSDavid Sebek request->data_length = unmapListSize;
5638b1d35bdSDavid Sebek request->sg_list = NULL;
5648b1d35bdSDavid Sebek
5658b1d35bdSDavid Sebek SHOW_FLOW(3, "UNMAP data used %" B_PRIu16
5668b1d35bdSDavid Sebek " of %" B_PRIuSIZE " allocated bytes",
5678b1d35bdSDavid Sebek unmapListSize, unmapListAllocatedSize);
5688b1d35bdSDavid Sebek
5698b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
5708b1d35bdSDavid Sebek uint16 scsiRangeCount = (uint16)B_BENDIAN_TO_HOST_INT16(
5718b1d35bdSDavid Sebek unmapList->block_data_length)
5728b1d35bdSDavid Sebek / sizeof(scsi_unmap_block_descriptor);
5738b1d35bdSDavid Sebek uint64 count = 0;
5748b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending an UNMAP command to"
5758b1d35bdSDavid Sebek " the device (blocks):\n");
5768b1d35bdSDavid Sebek for (uint16 i = 0; i < scsiRangeCount; i++) {
5778b1d35bdSDavid Sebek dprintf("[%3" B_PRIu16 "] %" B_PRIu64 " : %" B_PRIu32 "\n",
5788b1d35bdSDavid Sebek i, (uint64)B_BENDIAN_TO_HOST_INT64(
5798b1d35bdSDavid Sebek unmapList->blocks[i].lba),
5808b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(
5818b1d35bdSDavid Sebek unmapList->blocks[i].block_count));
5828b1d35bdSDavid Sebek count += (uint32)B_BENDIAN_TO_HOST_INT32(
5838b1d35bdSDavid Sebek unmapList->blocks[i].block_count);
5848b1d35bdSDavid Sebek }
5858b1d35bdSDavid Sebek if (device->max_unmap_lba_count >= count)
5868b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP command would fit %"
5878b1d35bdSDavid Sebek B_PRIu64 " more LBAs\n",
5888b1d35bdSDavid Sebek device->max_unmap_lba_count - count);
5898b1d35bdSDavid Sebek else
5908b1d35bdSDavid Sebek dprintf("TRIM: SCSI: Previous UNMAP ranges exceed the"
5918b1d35bdSDavid Sebek " device limit!\n");
5928b1d35bdSDavid Sebek #endif /* DEBUG_TRIM */
5938b1d35bdSDavid Sebek
5948b1d35bdSDavid Sebek status = periph_safe_exec(device, request);
595960c56aeSAxel Dörfler
596960c56aeSAxel Dörfler // peripheral layer only creates "read" error
597e6bde50aSAxel Dörfler if (status == B_DEV_READ_ERROR)
598960c56aeSAxel Dörfler return B_DEV_WRITE_ERROR;
5998b1d35bdSDavid Sebek else if (status != B_OK)
6008b1d35bdSDavid Sebek return status;
6018b1d35bdSDavid Sebek
6028b1d35bdSDavid Sebek *trimmedBlocks += trimmedBlocksInRequest;
6038b1d35bdSDavid Sebek
6048b1d35bdSDavid Sebek descriptorIndex = 0;
6058b1d35bdSDavid Sebek trimmedBlocksInRequest = 0;
6068b1d35bdSDavid Sebek memset(unmapList, 0, unmapListSize);
6078b1d35bdSDavid Sebek }
6088b1d35bdSDavid Sebek
6098b1d35bdSDavid Sebek length -= trimLength;
6108b1d35bdSDavid Sebek lba += trimLength;
6118b1d35bdSDavid Sebek }
6128b1d35bdSDavid Sebek }
613960c56aeSAxel Dörfler
614e6bde50aSAxel Dörfler return status;
615960c56aeSAxel Dörfler }
616960c56aeSAxel Dörfler
6178b1d35bdSDavid Sebek
6188b1d35bdSDavid Sebek static status_t
trim_writesame16(scsi_periph_device_info * device,scsi_ccb * request,scsi_block_range * ranges,uint32 rangeCount,uint64 * trimmedBlocks)6198b1d35bdSDavid Sebek trim_writesame16(scsi_periph_device_info* device, scsi_ccb* request,
6208b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
6218b1d35bdSDavid Sebek {
6228b1d35bdSDavid Sebek status_t status = B_OK;
6238b1d35bdSDavid Sebek *trimmedBlocks = 0;
6248b1d35bdSDavid Sebek
6258b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) {
6268b1d35bdSDavid Sebek uint64 lba = ranges[i].lba;
6278b1d35bdSDavid Sebek uint64 length = ranges[i].size;
6288b1d35bdSDavid Sebek
6298b1d35bdSDavid Sebek if (length == 0)
6308b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device!
6318b1d35bdSDavid Sebek
6328b1d35bdSDavid Sebek if (lba > WS16_MAX_LBA_VALUE) {
6338b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!"
6348b1d35bdSDavid Sebek " This unmap range will be skipped.");
6358b1d35bdSDavid Sebek continue;
6368b1d35bdSDavid Sebek }
6378b1d35bdSDavid Sebek
6388b1d35bdSDavid Sebek // Split the range into multiple requests if needed
6398b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count,
6408b1d35bdSDavid Sebek WS16_MAX_BLOCK_COUNT_VALUE);
6418b1d35bdSDavid Sebek while (length > 0) {
6428b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength);
6438b1d35bdSDavid Sebek if (trimLength == 0) {
6448b1d35bdSDavid Sebek SHOW_ERROR0(1,
6458b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (16) detected");
6468b1d35bdSDavid Sebek break;
6478b1d35bdSDavid Sebek }
6488b1d35bdSDavid Sebek
6498b1d35bdSDavid Sebek void* block = malloc(device->block_size);
6508b1d35bdSDavid Sebek if (block == NULL)
6518b1d35bdSDavid Sebek return B_NO_MEMORY;
6528b1d35bdSDavid Sebek MemoryDeleter deleter(block);
6538b1d35bdSDavid Sebek memset(block, 0, device->block_size);
6548b1d35bdSDavid Sebek
6558b1d35bdSDavid Sebek scsi_cmd_wsame_16* cmd = (scsi_cmd_wsame_16*)request->cdb;
6568b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd));
6578b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_16;
6588b1d35bdSDavid Sebek cmd->unmap = 1;
6598b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT64(lba);
6608b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT32(trimLength);
6618b1d35bdSDavid Sebek //cmd->ndob = 1; // no data is needed if this bit is enabled
6628b1d35bdSDavid Sebek
6638b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT;
6648b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd);
6658b1d35bdSDavid Sebek request->sort = lba;
6668b1d35bdSDavid Sebek request->timeout = device->std_timeout;
6678b1d35bdSDavid Sebek
6688b1d35bdSDavid Sebek request->data = (uint8*)block;
6698b1d35bdSDavid Sebek request->data_length = device->block_size;
6708b1d35bdSDavid Sebek request->sg_list = NULL;
6718b1d35bdSDavid Sebek
6728b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
6738b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (16) command to"
6748b1d35bdSDavid Sebek " the device (blocks):\n");
6758b1d35bdSDavid Sebek dprintf("%" B_PRIu64 " : %" B_PRIu32 "\n",
6768b1d35bdSDavid Sebek (uint64)B_BENDIAN_TO_HOST_INT64(cmd->lba),
6778b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->length));
6788b1d35bdSDavid Sebek #endif
6798b1d35bdSDavid Sebek
6808b1d35bdSDavid Sebek status = periph_safe_exec(device, request);
6818b1d35bdSDavid Sebek
6828b1d35bdSDavid Sebek // peripheral layer only creates "read" error
6838b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR)
6848b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR;
6858b1d35bdSDavid Sebek else if (status != B_OK)
6868b1d35bdSDavid Sebek return status;
6878b1d35bdSDavid Sebek
6888b1d35bdSDavid Sebek *trimmedBlocks += trimLength;
6898b1d35bdSDavid Sebek length -= trimLength;
6908b1d35bdSDavid Sebek lba += trimLength;
6918b1d35bdSDavid Sebek }
6928b1d35bdSDavid Sebek }
6938b1d35bdSDavid Sebek
6948b1d35bdSDavid Sebek return status;
6958b1d35bdSDavid Sebek }
6968b1d35bdSDavid Sebek
6978b1d35bdSDavid Sebek
6988b1d35bdSDavid Sebek static status_t
trim_writesame10(scsi_periph_device_info * device,scsi_ccb * request,scsi_block_range * ranges,uint32 rangeCount,uint64 * trimmedBlocks)6998b1d35bdSDavid Sebek trim_writesame10(scsi_periph_device_info* device, scsi_ccb* request,
7008b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
7018b1d35bdSDavid Sebek {
7028b1d35bdSDavid Sebek status_t status = B_OK;
7038b1d35bdSDavid Sebek *trimmedBlocks = 0;
7048b1d35bdSDavid Sebek
7058b1d35bdSDavid Sebek for (uint32 i = 0; i < rangeCount; i++) {
7068b1d35bdSDavid Sebek uint64 lba = ranges[i].lba;
7078b1d35bdSDavid Sebek uint64 length = ranges[i].size;
7088b1d35bdSDavid Sebek
7098b1d35bdSDavid Sebek if (length == 0)
7108b1d35bdSDavid Sebek continue; // length of 0 would mean the rest of the device!
7118b1d35bdSDavid Sebek
7128b1d35bdSDavid Sebek if (lba > WS10_MAX_LBA_VALUE) {
7138b1d35bdSDavid Sebek SHOW_ERROR0(1, "LBA value is too large!"
7148b1d35bdSDavid Sebek " This unmap range will be skipped.");
7158b1d35bdSDavid Sebek continue;
7168b1d35bdSDavid Sebek }
7178b1d35bdSDavid Sebek
7188b1d35bdSDavid Sebek // Split the range into multiple requests if needed
7198b1d35bdSDavid Sebek uint64 maxLength = min_c(device->max_unmap_lba_count,
7208b1d35bdSDavid Sebek WS10_MAX_BLOCK_COUNT_VALUE);
7218b1d35bdSDavid Sebek while (length > 0) {
7228b1d35bdSDavid Sebek uint64 trimLength = min_c(length, maxLength);
7238b1d35bdSDavid Sebek if (trimLength == 0) {
7248b1d35bdSDavid Sebek SHOW_ERROR0(1,
7258b1d35bdSDavid Sebek "Error: Length of zero in WRITE SAME (10) detected");
7268b1d35bdSDavid Sebek break;
7278b1d35bdSDavid Sebek }
7288b1d35bdSDavid Sebek
7298b1d35bdSDavid Sebek void* block = malloc(device->block_size);
7308b1d35bdSDavid Sebek if (block == NULL)
7318b1d35bdSDavid Sebek return B_NO_MEMORY;
7328b1d35bdSDavid Sebek MemoryDeleter deleter(block);
7338b1d35bdSDavid Sebek memset(block, 0, device->block_size);
7348b1d35bdSDavid Sebek
7358b1d35bdSDavid Sebek scsi_cmd_wsame_10* cmd = (scsi_cmd_wsame_10*)request->cdb;
7368b1d35bdSDavid Sebek memset(cmd, 0, sizeof(*cmd));
7378b1d35bdSDavid Sebek cmd->opcode = SCSI_OP_WRITE_SAME_10;
7388b1d35bdSDavid Sebek cmd->unmap = 1;
7398b1d35bdSDavid Sebek cmd->lba = B_HOST_TO_BENDIAN_INT32(lba);
7408b1d35bdSDavid Sebek cmd->length = B_HOST_TO_BENDIAN_INT16(trimLength);
7418b1d35bdSDavid Sebek
7428b1d35bdSDavid Sebek request->flags = SCSI_DIR_OUT;
7438b1d35bdSDavid Sebek request->cdb_length = sizeof(*cmd);
7448b1d35bdSDavid Sebek request->sort = lba;
7458b1d35bdSDavid Sebek request->timeout = device->std_timeout;
7468b1d35bdSDavid Sebek
7478b1d35bdSDavid Sebek request->data = (uint8*)block;
7488b1d35bdSDavid Sebek request->data_length = device->block_size;
7498b1d35bdSDavid Sebek request->sg_list = NULL;
7508b1d35bdSDavid Sebek
7518b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
7528b1d35bdSDavid Sebek dprintf("TRIM: SCSI: sending a WRITE SAME (10) command to"
7538b1d35bdSDavid Sebek " the device (blocks):\n");
7548b1d35bdSDavid Sebek dprintf("%" B_PRIu32 " : %" B_PRIu16 "\n",
7558b1d35bdSDavid Sebek (uint32)B_BENDIAN_TO_HOST_INT32(cmd->lba),
7568b1d35bdSDavid Sebek (uint16)B_BENDIAN_TO_HOST_INT16(cmd->length));
7578b1d35bdSDavid Sebek #endif
7588b1d35bdSDavid Sebek
7598b1d35bdSDavid Sebek status = periph_safe_exec(device, request);
7608b1d35bdSDavid Sebek
7618b1d35bdSDavid Sebek // peripheral layer only creates "read" error
7628b1d35bdSDavid Sebek if (status == B_DEV_READ_ERROR)
7638b1d35bdSDavid Sebek return B_DEV_WRITE_ERROR;
7648b1d35bdSDavid Sebek else if (status != B_OK)
7658b1d35bdSDavid Sebek return status;
7668b1d35bdSDavid Sebek
7678b1d35bdSDavid Sebek *trimmedBlocks += trimLength;
7688b1d35bdSDavid Sebek length -= trimLength;
7698b1d35bdSDavid Sebek lba += trimLength;
7708b1d35bdSDavid Sebek }
7718b1d35bdSDavid Sebek }
7728b1d35bdSDavid Sebek
7738b1d35bdSDavid Sebek return status;
7748b1d35bdSDavid Sebek }
7758b1d35bdSDavid Sebek
7768b1d35bdSDavid Sebek
7778b1d35bdSDavid Sebek status_t
periph_trim_device(scsi_periph_device_info * device,scsi_ccb * request,scsi_block_range * ranges,uint32 rangeCount,uint64 * trimmedBlocks)7788b1d35bdSDavid Sebek periph_trim_device(scsi_periph_device_info* device, scsi_ccb* request,
7798b1d35bdSDavid Sebek scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
7808b1d35bdSDavid Sebek {
7818b1d35bdSDavid Sebek *trimmedBlocks = 0;
7828b1d35bdSDavid Sebek
7838b1d35bdSDavid Sebek if (device->unmap_command == TRIM_NONE
7848b1d35bdSDavid Sebek || device->max_unmap_lba_count == 0
7858b1d35bdSDavid Sebek || device->max_unmap_descriptor_count == 0)
7868b1d35bdSDavid Sebek return B_UNSUPPORTED;
7878b1d35bdSDavid Sebek
7888b1d35bdSDavid Sebek switch (device->unmap_command) {
7898b1d35bdSDavid Sebek case TRIM_UNMAP:
7908b1d35bdSDavid Sebek return trim_unmap(device, request, ranges, rangeCount,
7918b1d35bdSDavid Sebek trimmedBlocks);
7928b1d35bdSDavid Sebek case TRIM_WRITESAME16:
7938b1d35bdSDavid Sebek return trim_writesame16(device, request, ranges, rangeCount,
7948b1d35bdSDavid Sebek trimmedBlocks);
7958b1d35bdSDavid Sebek case TRIM_WRITESAME10:
7968b1d35bdSDavid Sebek return trim_writesame10(device, request, ranges, rangeCount,
7978b1d35bdSDavid Sebek trimmedBlocks);
7988b1d35bdSDavid Sebek default:
7998b1d35bdSDavid Sebek return B_UNSUPPORTED;
8008b1d35bdSDavid Sebek }
8018b1d35bdSDavid Sebek }
802