xref: /haiku/src/add-ons/kernel/generic/scsi_periph/block.cpp (revision 2710b4f5d4251c5cf88c82b0114ea99b0ef46d22)
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