xref: /haiku/src/add-ons/kernel/generic/scsi_periph/block.cpp (revision 8b1d35bdbb2cc7dd27772fc08da71461d24b53fa)
124593e2cSAxel Dörfler /*
2*8b1d35bdSDavid Sebek  * Copyright 2021 David Sebek, dasebek@gmail.com
3*8b1d35bdSDavid Sebek  * Copyright 2004-2013 Haiku, Inc.
4*8b1d35bdSDavid Sebek  * Copyright 2002-2003 Thomas Kurschel
5*8b1d35bdSDavid 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 
19*8b1d35bdSDavid Sebek // UNMAP command limits
20*8b1d35bdSDavid Sebek #define UNMAP_MAX_LBA_VALUE				UINT64_MAX
21*8b1d35bdSDavid Sebek #define UNMAP_MAX_BLOCK_COUNT_VALUE		UINT32_MAX
22*8b1d35bdSDavid Sebek #define UNMAP_MAX_DESCRIPTORS			4095
23*8b1d35bdSDavid Sebek 	// Limit imposed by the UNMAP command structure
24*8b1d35bdSDavid Sebek #define UNMAP_DEFAULT_DESCRIPTORS		255
25*8b1d35bdSDavid Sebek 	// Reasonable default (?) when not specified by the device
26*8b1d35bdSDavid Sebek 
27*8b1d35bdSDavid Sebek // WRITE SAME (16) command limits
28*8b1d35bdSDavid Sebek #define WS16_MAX_LBA_VALUE				UINT64_MAX
29*8b1d35bdSDavid Sebek #define WS16_MAX_BLOCK_COUNT_VALUE		UINT32_MAX
30*8b1d35bdSDavid Sebek 
31*8b1d35bdSDavid Sebek // WRITE SAME (10) command limits
32*8b1d35bdSDavid Sebek #define WS10_MAX_LBA_VALUE				UINT32_MAX
33*8b1d35bdSDavid Sebek #define WS10_MAX_BLOCK_COUNT_VALUE		UINT16_MAX
34*8b1d35bdSDavid Sebek 
35*8b1d35bdSDavid Sebek 
36*8b1d35bdSDavid Sebek struct CapacityInfo {
37*8b1d35bdSDavid Sebek 	// Result of the READ CAPACITY command
38*8b1d35bdSDavid Sebek 	bool capacityFilled;
39*8b1d35bdSDavid Sebek 	uint64 lastLba;
40*8b1d35bdSDavid Sebek 	uint32 blockSize;
41*8b1d35bdSDavid Sebek 
42*8b1d35bdSDavid Sebek 	// Provisioining info from READ CAPACITY
43*8b1d35bdSDavid Sebek 	bool provisioningFilled;
44*8b1d35bdSDavid Sebek 	bool lbpme;
45*8b1d35bdSDavid Sebek 	bool lbprz;
46*8b1d35bdSDavid Sebek };
47*8b1d35bdSDavid Sebek 
48*8b1d35bdSDavid Sebek 
49*8b1d35bdSDavid Sebek struct UnmapSupport {
50*8b1d35bdSDavid Sebek 	// UNMAP commands supported by the device
51*8b1d35bdSDavid Sebek 	bool commandSupportFilled;
52*8b1d35bdSDavid Sebek 	bool unmapSupported;
53*8b1d35bdSDavid Sebek 	bool ws16Supported;
54*8b1d35bdSDavid Sebek 	bool ws10Supported;
55*8b1d35bdSDavid Sebek 
56*8b1d35bdSDavid Sebek 	// Block limits for UNMAP commands
57*8b1d35bdSDavid Sebek 	bool blockLimitsFilled;
58*8b1d35bdSDavid Sebek 	uint32 maxUnmapLbaCount;
59*8b1d35bdSDavid Sebek 	uint32 maxUnmapDescriptorCount;
60*8b1d35bdSDavid Sebek 	uint64 maxWritesameLength;
61*8b1d35bdSDavid Sebek };
62*8b1d35bdSDavid Sebek 
63*8b1d35bdSDavid Sebek 
64*8b1d35bdSDavid Sebek static bool
65*8b1d35bdSDavid Sebek prefer_read_capacity_16(scsi_periph_device_info* device)
66*8b1d35bdSDavid Sebek {
67*8b1d35bdSDavid Sebek 	const scsi_res_inquiry* inquiryData = NULL;
68*8b1d35bdSDavid Sebek 	size_t inquiryDataLength;
69*8b1d35bdSDavid Sebek 
70*8b1d35bdSDavid Sebek 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
71*8b1d35bdSDavid Sebek 				(const void**)&inquiryData, &inquiryDataLength, true) != B_OK
72*8b1d35bdSDavid Sebek 		|| inquiryDataLength != sizeof(*inquiryData)) {
73*8b1d35bdSDavid Sebek 		return false;
74*8b1d35bdSDavid Sebek 	}
75*8b1d35bdSDavid Sebek 
76*8b1d35bdSDavid Sebek 	if (inquiryData->protect)
77*8b1d35bdSDavid Sebek 		return true;
78*8b1d35bdSDavid Sebek 
79*8b1d35bdSDavid Sebek 	if (inquiryData->ansi_version > 0x04 /* SPC-2 */)
80*8b1d35bdSDavid Sebek 		return true;
81*8b1d35bdSDavid Sebek 
82*8b1d35bdSDavid Sebek 	return false;
83*8b1d35bdSDavid Sebek }
84*8b1d35bdSDavid Sebek 
85*8b1d35bdSDavid Sebek 
86*8b1d35bdSDavid Sebek static bool
87*8b1d35bdSDavid Sebek vpd_pages_supported(scsi_periph_device_info* device)
88*8b1d35bdSDavid Sebek {
89*8b1d35bdSDavid Sebek 	const scsi_res_inquiry* inquiryData = NULL;
90*8b1d35bdSDavid Sebek 	size_t inquiryDataLength;
91*8b1d35bdSDavid Sebek 
92*8b1d35bdSDavid Sebek 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
93*8b1d35bdSDavid Sebek 				(const void**)&inquiryData, &inquiryDataLength, true) != B_OK
94*8b1d35bdSDavid Sebek 		|| inquiryDataLength != sizeof(*inquiryData)) {
95*8b1d35bdSDavid Sebek 		return false;
96*8b1d35bdSDavid Sebek 	}
97*8b1d35bdSDavid Sebek 
98*8b1d35bdSDavid Sebek 	if (inquiryData->ansi_version >= 0x04 /* SPC-2 */)
99*8b1d35bdSDavid Sebek 		return true;
100*8b1d35bdSDavid Sebek 
101*8b1d35bdSDavid Sebek 	return false;
102*8b1d35bdSDavid Sebek }
103*8b1d35bdSDavid Sebek 
104*8b1d35bdSDavid Sebek 
105*8b1d35bdSDavid Sebek static status_t
106*8b1d35bdSDavid Sebek read_capacity_10(scsi_periph_device_info* device, scsi_ccb* request,
107*8b1d35bdSDavid Sebek 	CapacityInfo* capacityInfo)
108*8b1d35bdSDavid Sebek {
109*8b1d35bdSDavid Sebek 	capacityInfo->capacityFilled = false;
110*8b1d35bdSDavid Sebek 	capacityInfo->provisioningFilled = false;
111*8b1d35bdSDavid Sebek 
112*8b1d35bdSDavid Sebek 	scsi_res_read_capacity capacityResult;
113*8b1d35bdSDavid Sebek 	memset(&capacityResult, 0, sizeof(capacityResult));
114*8b1d35bdSDavid Sebek 
115*8b1d35bdSDavid Sebek 	scsi_cmd_read_capacity* cmd = (scsi_cmd_read_capacity*)request->cdb;
116*8b1d35bdSDavid Sebek 	memset(cmd, 0, sizeof(*cmd));
117*8b1d35bdSDavid Sebek 	cmd->opcode = SCSI_OP_READ_CAPACITY;
118*8b1d35bdSDavid Sebek 	// we don't set PMI (partial medium indicator) as we want the whole capacity;
119*8b1d35bdSDavid Sebek 	// in this case, all other parameters must be zero
120*8b1d35bdSDavid Sebek 
121*8b1d35bdSDavid Sebek 	request->flags = SCSI_DIR_IN;
122*8b1d35bdSDavid Sebek 	request->cdb_length = sizeof(*cmd);
123*8b1d35bdSDavid Sebek 	request->sort = -1;
124*8b1d35bdSDavid Sebek 	request->timeout = device->std_timeout;
125*8b1d35bdSDavid Sebek 
126*8b1d35bdSDavid Sebek 	request->data = (uint8*)&capacityResult;
127*8b1d35bdSDavid Sebek 	request->data_length = sizeof(capacityResult);
128*8b1d35bdSDavid Sebek 	request->sg_list = NULL;
129*8b1d35bdSDavid Sebek 
130*8b1d35bdSDavid Sebek 	status_t res = periph_safe_exec(device, request);
131*8b1d35bdSDavid Sebek 
132*8b1d35bdSDavid Sebek 	if (res == B_OK && request->data_resid == 0) {
133*8b1d35bdSDavid Sebek 		capacityInfo->capacityFilled = true;
134*8b1d35bdSDavid Sebek 		capacityInfo->lastLba
135*8b1d35bdSDavid Sebek 			= (uint32)B_BENDIAN_TO_HOST_INT32(capacityResult.lba);
136*8b1d35bdSDavid Sebek 		capacityInfo->blockSize
137*8b1d35bdSDavid Sebek 			= B_BENDIAN_TO_HOST_INT32(capacityResult.block_size);
138*8b1d35bdSDavid Sebek 	}
139*8b1d35bdSDavid Sebek 
140*8b1d35bdSDavid Sebek 	return res;
141*8b1d35bdSDavid Sebek }
142*8b1d35bdSDavid Sebek 
143*8b1d35bdSDavid Sebek 
144*8b1d35bdSDavid Sebek static status_t
145*8b1d35bdSDavid Sebek read_capacity_16(scsi_periph_device_info* device, scsi_ccb* request,
146*8b1d35bdSDavid Sebek 	CapacityInfo* capacityInfo)
147*8b1d35bdSDavid Sebek {
148*8b1d35bdSDavid Sebek 	capacityInfo->capacityFilled = false;
149*8b1d35bdSDavid Sebek 	capacityInfo->provisioningFilled = false;
150*8b1d35bdSDavid Sebek 
151*8b1d35bdSDavid Sebek 	scsi_res_read_capacity_long capacityLongResult;
152*8b1d35bdSDavid Sebek 	memset(&capacityLongResult, 0, sizeof(capacityLongResult));
153*8b1d35bdSDavid Sebek 
154*8b1d35bdSDavid Sebek 	scsi_cmd_read_capacity_long* cmd
155*8b1d35bdSDavid Sebek 		= (scsi_cmd_read_capacity_long*)request->cdb;
156*8b1d35bdSDavid Sebek 	memset(cmd, 0, sizeof(*cmd));
157*8b1d35bdSDavid Sebek 	cmd->opcode = SCSI_OP_SERVICE_ACTION_IN;
158*8b1d35bdSDavid Sebek 	cmd->service_action = SCSI_SAI_READ_CAPACITY_16;
159*8b1d35bdSDavid Sebek 	cmd->alloc_length = B_HOST_TO_BENDIAN_INT32(sizeof(capacityLongResult));
160*8b1d35bdSDavid Sebek 
161*8b1d35bdSDavid Sebek 	request->flags = SCSI_DIR_IN;
162*8b1d35bdSDavid Sebek 	request->cdb_length = sizeof(*cmd);
163*8b1d35bdSDavid Sebek 	request->sort = -1;
164*8b1d35bdSDavid Sebek 	request->timeout = device->std_timeout;
165*8b1d35bdSDavid Sebek 
166*8b1d35bdSDavid Sebek 	request->data = (uint8*)&capacityLongResult;
167*8b1d35bdSDavid Sebek 	request->data_length = sizeof(capacityLongResult);
168*8b1d35bdSDavid Sebek 	request->sg_list = NULL;
169*8b1d35bdSDavid Sebek 
170*8b1d35bdSDavid Sebek 	status_t res = periph_safe_exec(device, request);
171*8b1d35bdSDavid Sebek 
172*8b1d35bdSDavid Sebek 	if (res == B_OK && request->data_resid
173*8b1d35bdSDavid Sebek 			<= (int32)sizeof(scsi_res_read_capacity_long) - 12) {
174*8b1d35bdSDavid Sebek 		// At least the last LBA and sector size have been transfered
175*8b1d35bdSDavid Sebek 		capacityInfo->capacityFilled = true;
176*8b1d35bdSDavid Sebek 		capacityInfo->lastLba
177*8b1d35bdSDavid Sebek 			= B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba);
178*8b1d35bdSDavid Sebek 		capacityInfo->blockSize
179*8b1d35bdSDavid Sebek 			= B_BENDIAN_TO_HOST_INT32(capacityLongResult.block_size);
180*8b1d35bdSDavid Sebek 	}
181*8b1d35bdSDavid Sebek 
182*8b1d35bdSDavid Sebek 	if (res == B_OK && request->data_resid
183*8b1d35bdSDavid Sebek 			<= (int32)sizeof(scsi_res_read_capacity_long) - 15) {
184*8b1d35bdSDavid Sebek 		// lbpme and lbprz bits were received too
185*8b1d35bdSDavid Sebek 		capacityInfo->provisioningFilled = true;
186*8b1d35bdSDavid Sebek 		capacityInfo->lbpme = capacityLongResult.lbpme;
187*8b1d35bdSDavid Sebek 		capacityInfo->lbprz = capacityLongResult.lbprz;
188*8b1d35bdSDavid Sebek 	}
189*8b1d35bdSDavid Sebek 
190*8b1d35bdSDavid Sebek 	return res;
191*8b1d35bdSDavid Sebek }
192*8b1d35bdSDavid Sebek 
193*8b1d35bdSDavid Sebek 
194*8b1d35bdSDavid Sebek static status_t
195*8b1d35bdSDavid Sebek get_unmap_commands(scsi_periph_device_info* device, scsi_ccb* request,
196*8b1d35bdSDavid Sebek 	UnmapSupport* unmapSupport)
197*8b1d35bdSDavid Sebek {
198*8b1d35bdSDavid Sebek 	unmapSupport->commandSupportFilled = false;
199*8b1d35bdSDavid Sebek 
200*8b1d35bdSDavid Sebek 	scsi_page_lb_provisioning vpdProvisioning;
201*8b1d35bdSDavid Sebek 	memset(&vpdProvisioning, 0, sizeof(vpdProvisioning));
202*8b1d35bdSDavid Sebek 	status_t vpdStatus = vpd_page_get(device, request,
203*8b1d35bdSDavid Sebek 		SCSI_PAGE_LB_PROVISIONING, &vpdProvisioning, sizeof(vpdProvisioning));
204*8b1d35bdSDavid Sebek 
205*8b1d35bdSDavid Sebek 	if (vpdStatus == B_OK
206*8b1d35bdSDavid Sebek 		&& request->data_resid <= (int32)sizeof(scsi_page_lb_provisioning) - 6
207*8b1d35bdSDavid Sebek 		&& vpdProvisioning.page_code == SCSI_PAGE_LB_PROVISIONING
208*8b1d35bdSDavid Sebek 		&& B_BENDIAN_TO_HOST_INT16(vpdProvisioning.page_length) >= 2) {
209*8b1d35bdSDavid Sebek 		unmapSupport->commandSupportFilled = true;
210*8b1d35bdSDavid Sebek 		unmapSupport->unmapSupported = vpdProvisioning.lbpu;
211*8b1d35bdSDavid Sebek 		unmapSupport->ws16Supported = vpdProvisioning.lbpws;
212*8b1d35bdSDavid Sebek 		unmapSupport->ws10Supported = vpdProvisioning.lbpws10;
213*8b1d35bdSDavid Sebek 	}
214*8b1d35bdSDavid Sebek 
215*8b1d35bdSDavid Sebek 	if (vpdStatus == B_BAD_VALUE)
216*8b1d35bdSDavid Sebek 		return B_ERROR;
217*8b1d35bdSDavid Sebek 
218*8b1d35bdSDavid Sebek 	return vpdStatus;
219*8b1d35bdSDavid Sebek }
220*8b1d35bdSDavid Sebek 
221*8b1d35bdSDavid Sebek 
222*8b1d35bdSDavid Sebek static status_t
223*8b1d35bdSDavid Sebek get_unmap_limits(scsi_periph_device_info* device, scsi_ccb* request,
224*8b1d35bdSDavid Sebek 	UnmapSupport* unmapSupport)
225*8b1d35bdSDavid Sebek {
226*8b1d35bdSDavid Sebek 	unmapSupport->blockLimitsFilled = false;
227*8b1d35bdSDavid Sebek 
228*8b1d35bdSDavid Sebek 	scsi_page_block_limits vpdBlockLimits;
229*8b1d35bdSDavid Sebek 	memset(&vpdBlockLimits, 0, sizeof(vpdBlockLimits));
230*8b1d35bdSDavid Sebek 	status_t vpdStatus = vpd_page_get(device, request,
231*8b1d35bdSDavid Sebek 		SCSI_PAGE_BLOCK_LIMITS, &vpdBlockLimits, sizeof(vpdBlockLimits));
232*8b1d35bdSDavid Sebek 
233*8b1d35bdSDavid Sebek 	if (vpdStatus == B_OK
234*8b1d35bdSDavid Sebek 		&& request->data_resid <= (int32)sizeof(scsi_page_block_limits) - 44
235*8b1d35bdSDavid Sebek 		&& vpdBlockLimits.page_code == SCSI_PAGE_BLOCK_LIMITS
236*8b1d35bdSDavid Sebek 		&& B_BENDIAN_TO_HOST_INT16(vpdBlockLimits.page_length) == 0x3c) {
237*8b1d35bdSDavid Sebek 		unmapSupport->blockLimitsFilled = true;
238*8b1d35bdSDavid Sebek 		unmapSupport->maxUnmapLbaCount = B_BENDIAN_TO_HOST_INT32(
239*8b1d35bdSDavid Sebek 			vpdBlockLimits.max_unmap_lba_count);
240*8b1d35bdSDavid Sebek 		unmapSupport->maxUnmapDescriptorCount = B_BENDIAN_TO_HOST_INT32(
241*8b1d35bdSDavid Sebek 			vpdBlockLimits.max_unmap_blk_count);
242*8b1d35bdSDavid Sebek 		unmapSupport->maxWritesameLength = B_BENDIAN_TO_HOST_INT64(
243*8b1d35bdSDavid Sebek 			vpdBlockLimits.max_write_same_length);
244*8b1d35bdSDavid Sebek 	}
245*8b1d35bdSDavid Sebek 
246*8b1d35bdSDavid Sebek 	if (vpdStatus == B_BAD_VALUE)
247*8b1d35bdSDavid Sebek 		return B_ERROR;
248*8b1d35bdSDavid Sebek 
249*8b1d35bdSDavid Sebek 	return vpdStatus;
250*8b1d35bdSDavid Sebek }
251*8b1d35bdSDavid Sebek 
252*8b1d35bdSDavid Sebek 
253*8b1d35bdSDavid Sebek static void
254*8b1d35bdSDavid Sebek determine_unmap_support(const UnmapSupport* unmapSupport,
255*8b1d35bdSDavid Sebek 	enum trim_command* unmapCommand, uint32* maxLbaCount,
256*8b1d35bdSDavid Sebek 	uint32* maxDescriptorCount)
257*8b1d35bdSDavid Sebek {
258*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
259*8b1d35bdSDavid Sebek 	if (unmapSupport->commandSupportFilled)
260*8b1d35bdSDavid Sebek 		dprintf("TRIM: device reports (LBP VPD): LBPU = %d, LBPWS = %d,"
261*8b1d35bdSDavid Sebek 			" LBPWS10 = %d\n", unmapSupport->unmapSupported,
262*8b1d35bdSDavid Sebek 			unmapSupport->ws16Supported, unmapSupport->ws10Supported);
263*8b1d35bdSDavid Sebek 	else
264*8b1d35bdSDavid Sebek 		dprintf("TRIM: could not get the LBP VPD of the device\n");
265*8b1d35bdSDavid Sebek 	if (unmapSupport->blockLimitsFilled)
266*8b1d35bdSDavid Sebek 		dprintf("TRIM: device reports (Block Limits VPD):"
267*8b1d35bdSDavid Sebek 			"\nTRIM: MAXIMUM UNMAP LBA COUNT = %" B_PRIu32
268*8b1d35bdSDavid Sebek 			"\nTRIM: MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT = %" B_PRIu32
269*8b1d35bdSDavid Sebek 			"\nTRIM: MAXIMUM WRITESAME LENGTH = %" B_PRIu64 "\n",
270*8b1d35bdSDavid Sebek 			unmapSupport->maxUnmapLbaCount,
271*8b1d35bdSDavid Sebek 			unmapSupport->maxUnmapDescriptorCount,
272*8b1d35bdSDavid Sebek 			unmapSupport->maxWritesameLength);
273*8b1d35bdSDavid Sebek 	else
274*8b1d35bdSDavid Sebek 		dprintf("TRIM: could not get Block Limits VPD of the device\n");
275*8b1d35bdSDavid Sebek #endif
276*8b1d35bdSDavid Sebek 
277*8b1d35bdSDavid Sebek 	*unmapCommand = TRIM_NONE;
278*8b1d35bdSDavid Sebek 	*maxLbaCount = 0;
279*8b1d35bdSDavid Sebek 	*maxDescriptorCount = 0;
280*8b1d35bdSDavid Sebek 
281*8b1d35bdSDavid Sebek 	if (!unmapSupport->commandSupportFilled
282*8b1d35bdSDavid Sebek 		|| !unmapSupport->blockLimitsFilled)
283*8b1d35bdSDavid Sebek 		return;
284*8b1d35bdSDavid Sebek 
285*8b1d35bdSDavid Sebek 	if (unmapSupport->unmapSupported
286*8b1d35bdSDavid Sebek 		&& unmapSupport->maxUnmapLbaCount > 0
287*8b1d35bdSDavid Sebek 		&& unmapSupport->maxUnmapDescriptorCount > 0) {
288*8b1d35bdSDavid Sebek 		*unmapCommand = TRIM_UNMAP;
289*8b1d35bdSDavid Sebek 		*maxLbaCount = unmapSupport->maxUnmapLbaCount;
290*8b1d35bdSDavid Sebek 		if (unmapSupport->maxUnmapDescriptorCount == UINT32_MAX
291*8b1d35bdSDavid Sebek 			|| unmapSupport->maxUnmapDescriptorCount > UNMAP_MAX_DESCRIPTORS) {
292*8b1d35bdSDavid Sebek 			// Choose a reasonable value instead
293*8b1d35bdSDavid Sebek 			*maxDescriptorCount = UNMAP_DEFAULT_DESCRIPTORS;
294*8b1d35bdSDavid Sebek 		} else {
295*8b1d35bdSDavid Sebek 			*maxDescriptorCount = unmapSupport->maxUnmapDescriptorCount;
296*8b1d35bdSDavid Sebek 		}
297*8b1d35bdSDavid Sebek 	}
298*8b1d35bdSDavid Sebek 
299*8b1d35bdSDavid Sebek 	if (*unmapCommand == TRIM_NONE && unmapSupport->ws16Supported) {
300*8b1d35bdSDavid Sebek 		uint64 maxLength = unmapSupport->maxWritesameLength;
301*8b1d35bdSDavid Sebek 		if (maxLength == 0) {
302*8b1d35bdSDavid Sebek 			// WRITE SAME limit not reported, try UNMAP limit instead
303*8b1d35bdSDavid Sebek 			if (unmapSupport->maxUnmapLbaCount > 0)
304*8b1d35bdSDavid Sebek 				maxLength = unmapSupport->maxUnmapLbaCount;
305*8b1d35bdSDavid Sebek 			else
306*8b1d35bdSDavid Sebek 				maxLength = WS16_MAX_BLOCK_COUNT_VALUE;
307*8b1d35bdSDavid Sebek 		}
308*8b1d35bdSDavid Sebek 		*unmapCommand = TRIM_WRITESAME16;
309*8b1d35bdSDavid Sebek 		*maxLbaCount = min_c(maxLength, WS16_MAX_BLOCK_COUNT_VALUE);
310*8b1d35bdSDavid Sebek 		*maxDescriptorCount = 1;
311*8b1d35bdSDavid Sebek 	}
312*8b1d35bdSDavid Sebek 
313*8b1d35bdSDavid Sebek 	if (*unmapCommand == TRIM_NONE && unmapSupport->ws10Supported) {
314*8b1d35bdSDavid Sebek 		uint64 maxLength = unmapSupport->maxWritesameLength;
315*8b1d35bdSDavid Sebek 		if (maxLength == 0) {
316*8b1d35bdSDavid Sebek 			// WRITE SAME limit not reported, try UNMAP limit instead
317*8b1d35bdSDavid Sebek 			if (unmapSupport->maxUnmapLbaCount > 0)
318*8b1d35bdSDavid Sebek 				maxLength = unmapSupport->maxUnmapLbaCount;
319*8b1d35bdSDavid Sebek 			else
320*8b1d35bdSDavid Sebek 				maxLength = WS10_MAX_BLOCK_COUNT_VALUE;
321*8b1d35bdSDavid Sebek 		}
322*8b1d35bdSDavid Sebek 		*unmapCommand = TRIM_WRITESAME10;
323*8b1d35bdSDavid Sebek 		*maxLbaCount = min_c(maxLength, WS10_MAX_BLOCK_COUNT_VALUE);
324*8b1d35bdSDavid Sebek 		*maxDescriptorCount = 1;
325*8b1d35bdSDavid Sebek 	}
326*8b1d35bdSDavid Sebek }
327*8b1d35bdSDavid Sebek 
328*8b1d35bdSDavid Sebek 
32924593e2cSAxel Dörfler status_t
33024593e2cSAxel Dörfler periph_check_capacity(scsi_periph_device_info* device, scsi_ccb* request)
33124593e2cSAxel Dörfler {
332*8b1d35bdSDavid Sebek 	CapacityInfo capacityInfo = {0};
33324593e2cSAxel Dörfler 	status_t res;
33424593e2cSAxel Dörfler 
33524593e2cSAxel Dörfler 	SHOW_FLOW(3, "%p, %p", device, request);
33624593e2cSAxel Dörfler 
33724593e2cSAxel Dörfler 	// driver doesn't support capacity callback - seems to be no block
33824593e2cSAxel Dörfler 	// device driver, so ignore
33924593e2cSAxel Dörfler 	if (device->callbacks->set_capacity == NULL)
34024593e2cSAxel Dörfler 		return B_OK;
34124593e2cSAxel Dörfler 
342*8b1d35bdSDavid Sebek 	if (prefer_read_capacity_16(device)) {
343*8b1d35bdSDavid Sebek 		SHOW_FLOW0(3, "READ CAPACITY 16 tried first");
344*8b1d35bdSDavid Sebek 		res = read_capacity_16(device, request, &capacityInfo);
34524593e2cSAxel Dörfler 
346*8b1d35bdSDavid Sebek 		if (res == B_ERROR) {
347*8b1d35bdSDavid Sebek 			SHOW_FLOW0(3, "READ CAPACITY 16 failed, trying READ CAPACITY 10");
348*8b1d35bdSDavid Sebek 			res = read_capacity_10(device, request, &capacityInfo);
349*8b1d35bdSDavid Sebek 		}
350*8b1d35bdSDavid Sebek 	} else {
351*8b1d35bdSDavid Sebek 		SHOW_FLOW0(3, "READ CAPACITY 10 tried first");
352*8b1d35bdSDavid Sebek 		res = read_capacity_10(device, request, &capacityInfo);
35324593e2cSAxel Dörfler 
354*8b1d35bdSDavid Sebek 		if (res == B_OK && capacityInfo.capacityFilled
355*8b1d35bdSDavid Sebek 			&& capacityInfo.lastLba == UINT32_MAX) {
356*8b1d35bdSDavid Sebek 			SHOW_FLOW0(3, "Device is too large, trying READ CAPACITY 16");
357*8b1d35bdSDavid Sebek 			res = read_capacity_16(device, request, &capacityInfo);
358*8b1d35bdSDavid Sebek 		}
359*8b1d35bdSDavid Sebek 	}
36024593e2cSAxel Dörfler 
361*8b1d35bdSDavid Sebek 	uint64 capacity;
362*8b1d35bdSDavid Sebek 	uint32 blockSize;
363*8b1d35bdSDavid Sebek 
364*8b1d35bdSDavid Sebek 	if (capacityInfo.capacityFilled) {
365*8b1d35bdSDavid Sebek 		capacity = capacityInfo.lastLba + 1;
366*8b1d35bdSDavid Sebek 		blockSize = capacityInfo.blockSize;
367*8b1d35bdSDavid Sebek 	} else {
368*8b1d35bdSDavid Sebek 		capacity = 0;
369*8b1d35bdSDavid Sebek 		blockSize = 0;
370*8b1d35bdSDavid Sebek 	}
371*8b1d35bdSDavid Sebek 
372*8b1d35bdSDavid Sebek 	enum trim_command unmapCommand = TRIM_NONE;
373*8b1d35bdSDavid Sebek 	uint32 maxLbaCount = 0;
374*8b1d35bdSDavid Sebek 	uint32 maxDescriptorCount = 0;
375*8b1d35bdSDavid Sebek 
376*8b1d35bdSDavid Sebek 	if (capacityInfo.provisioningFilled
377*8b1d35bdSDavid Sebek 		&& capacityInfo.lbpme
378*8b1d35bdSDavid Sebek 		&& vpd_pages_supported(device)) {
379*8b1d35bdSDavid Sebek 		UnmapSupport unmapSupport = {0};
380*8b1d35bdSDavid Sebek 
381*8b1d35bdSDavid Sebek 		// Don't fail if the device doesn't support the command
382*8b1d35bdSDavid Sebek 		// but fail if some other error happens
383*8b1d35bdSDavid Sebek 		if (res == B_OK) {
384*8b1d35bdSDavid Sebek 			status_t vpdStatus = get_unmap_commands(device, request,
385*8b1d35bdSDavid Sebek 				&unmapSupport);
386*8b1d35bdSDavid Sebek 			if (vpdStatus != B_OK && vpdStatus != B_ERROR)
387*8b1d35bdSDavid Sebek 				res = vpdStatus;
388*8b1d35bdSDavid Sebek 		}
389*8b1d35bdSDavid Sebek 
390*8b1d35bdSDavid Sebek 		if (res == B_OK) {
391*8b1d35bdSDavid Sebek 			status_t vpdStatus = get_unmap_limits(device, request,
392*8b1d35bdSDavid Sebek 				&unmapSupport);
393*8b1d35bdSDavid Sebek 			if (vpdStatus != B_OK && vpdStatus != B_ERROR)
394*8b1d35bdSDavid Sebek 				res = vpdStatus;
395*8b1d35bdSDavid Sebek 		}
396*8b1d35bdSDavid Sebek 
397*8b1d35bdSDavid Sebek 		determine_unmap_support(&unmapSupport, &unmapCommand,
398*8b1d35bdSDavid Sebek 				&maxLbaCount, &maxDescriptorCount);
399*8b1d35bdSDavid Sebek 
400*8b1d35bdSDavid Sebek 		if (maxLbaCount == 0 || maxDescriptorCount == 0)
401*8b1d35bdSDavid Sebek 			unmapCommand = TRIM_NONE;
402*8b1d35bdSDavid Sebek 	}
40324593e2cSAxel Dörfler 
40424593e2cSAxel Dörfler 	if (res == B_DEV_MEDIA_CHANGED) {
40524593e2cSAxel Dörfler 		// in this case, the error handler has already called check_capacity
40624593e2cSAxel Dörfler 		// recursively, so we ignore our (invalid) result
40724593e2cSAxel Dörfler 		SHOW_FLOW0(3, "ignore result because medium change");
40824593e2cSAxel Dörfler 		return B_DEV_MEDIA_CHANGED;
40924593e2cSAxel Dörfler 	}
41024593e2cSAxel Dörfler 
411*8b1d35bdSDavid Sebek 	if (res == B_OK && !capacityInfo.capacityFilled)
412*8b1d35bdSDavid Sebek 		// Although the capacity and block size will be set to 0 in this case,
413*8b1d35bdSDavid Sebek 		// it is also better to inform the caller that these values were not
414*8b1d35bdSDavid Sebek 		// reported by the device
415*8b1d35bdSDavid Sebek 		res = B_ERROR;
41624593e2cSAxel Dörfler 
417*8b1d35bdSDavid Sebek 	SHOW_FLOW(3, "capacity = %" B_PRIu64 ", block_size = %" B_PRIu32
418*8b1d35bdSDavid Sebek 		" (%sreported)", capacity, blockSize,
419*8b1d35bdSDavid Sebek 		capacityInfo.capacityFilled ? "" : "not ");
420*8b1d35bdSDavid Sebek 	SHOW_INFO(1, "TRIM: Setting trim support to %s",
421*8b1d35bdSDavid Sebek 		unmapCommand == TRIM_NONE ? "disabled"
422*8b1d35bdSDavid Sebek 			: unmapCommand == TRIM_UNMAP ? "UNMAP"
423*8b1d35bdSDavid Sebek 			: unmapCommand == TRIM_WRITESAME16 ? "WRITE SAME (16)"
424*8b1d35bdSDavid Sebek 			: unmapCommand == TRIM_WRITESAME10 ? "WRITE SAME (10)"
425*8b1d35bdSDavid Sebek 			: "unknown");
426*8b1d35bdSDavid Sebek 	SHOW_FLOW(3, "TRIM: Block limits: size = %" B_PRIu32
427*8b1d35bdSDavid Sebek 		", descriptors = %" B_PRIu32, maxLbaCount, maxDescriptorCount);
428f10a55a6SJérôme Duval 
429a1e8da41SAxel Dörfler 	mutex_lock(&device->mutex);
430*8b1d35bdSDavid Sebek 		// Was there a reason why this mutex
431*8b1d35bdSDavid Sebek 		// was previously locked much earlier?
432f10a55a6SJérôme Duval 
433*8b1d35bdSDavid Sebek 	device->unmap_command = unmapCommand;
434*8b1d35bdSDavid Sebek 	device->max_unmap_lba_count = maxLbaCount;
435*8b1d35bdSDavid Sebek 	device->max_unmap_descriptor_count = maxDescriptorCount;
43624593e2cSAxel Dörfler 
43724593e2cSAxel Dörfler 	device->block_size = blockSize;
43824593e2cSAxel Dörfler 
43924593e2cSAxel Dörfler 	device->callbacks->set_capacity(device->periph_device,
44024593e2cSAxel Dörfler 		capacity, blockSize);
44124593e2cSAxel Dörfler 
44224593e2cSAxel Dörfler /*	device->byte2blk_shift = log2( device->block_size );
44324593e2cSAxel Dörfler 	if( device->byte2blk_shift < 0 ) {
44424593e2cSAxel Dörfler 		// this may be too restrictive...
44524593e2cSAxel Dörfler 		device->capacity = -1;
44624593e2cSAxel Dörfler 		return ERR_DEV_GENERAL;
44724593e2cSAxel Dörfler 	}*/
44824593e2cSAxel Dörfler 
449a1e8da41SAxel Dörfler 	mutex_unlock(&device->mutex);
45024593e2cSAxel Dörfler 
45124593e2cSAxel Dörfler 	SHOW_FLOW(3, "done (%s)", strerror(res));
45224593e2cSAxel Dörfler 
45324593e2cSAxel Dörfler 	return res;
45424593e2cSAxel Dörfler }
45524593e2cSAxel Dörfler 
456960c56aeSAxel Dörfler 
457*8b1d35bdSDavid Sebek static status_t
458*8b1d35bdSDavid Sebek trim_unmap(scsi_periph_device_info* device, scsi_ccb* request,
459*8b1d35bdSDavid Sebek 	scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
460960c56aeSAxel Dörfler {
461*8b1d35bdSDavid Sebek 	uint64 maxLength = UNMAP_MAX_BLOCK_COUNT_VALUE;
462*8b1d35bdSDavid Sebek 	uint64 maxBlocksInRequest = device->max_unmap_lba_count;
463*8b1d35bdSDavid Sebek 	uint32 maxDescriptors = device->max_unmap_descriptor_count;
464*8b1d35bdSDavid Sebek 
465*8b1d35bdSDavid Sebek 	*trimmedBlocks = 0;
466*8b1d35bdSDavid Sebek 
467*8b1d35bdSDavid Sebek 	// Allocate a single buffer and re-use it between requests
468*8b1d35bdSDavid Sebek 	size_t expectedDescriptorCount = 0;
469*8b1d35bdSDavid Sebek 	for (uint32 i = 0; i < rangeCount; i++) {
470*8b1d35bdSDavid Sebek 		expectedDescriptorCount += ranges[i].size / maxLength;
471*8b1d35bdSDavid Sebek 		if (ranges[i].size % maxLength != 0)
472*8b1d35bdSDavid Sebek 			expectedDescriptorCount++;
473*8b1d35bdSDavid Sebek 	}
474*8b1d35bdSDavid Sebek 	expectedDescriptorCount = min_c(expectedDescriptorCount, maxDescriptors);
475*8b1d35bdSDavid Sebek 
476*8b1d35bdSDavid Sebek 	size_t unmapListAllocatedSize = (expectedDescriptorCount - 1)
47799086aa3SAxel Dörfler 			* sizeof(scsi_unmap_block_descriptor)
47899086aa3SAxel Dörfler 		+ sizeof(scsi_unmap_parameter_list);
47999086aa3SAxel Dörfler 
480*8b1d35bdSDavid Sebek 	scsi_unmap_parameter_list* unmapList
481*8b1d35bdSDavid Sebek 		= (scsi_unmap_parameter_list*)malloc(unmapListAllocatedSize);
482*8b1d35bdSDavid Sebek 	if (unmapList == NULL)
48399086aa3SAxel Dörfler 		return B_NO_MEMORY;
48499086aa3SAxel Dörfler 
485*8b1d35bdSDavid Sebek 	MemoryDeleter deleter(unmapList);
48699086aa3SAxel Dörfler 
487*8b1d35bdSDavid Sebek 	status_t status = B_OK;
488*8b1d35bdSDavid Sebek 	uint32 descriptorIndex = 0;
489*8b1d35bdSDavid Sebek 	uint64 trimmedBlocksInRequest = 0;
490*8b1d35bdSDavid Sebek 	memset(unmapList, 0, unmapListAllocatedSize);
49199086aa3SAxel Dörfler 	for (uint32 i = 0; i < rangeCount; i++) {
492*8b1d35bdSDavid Sebek 		uint64 lba = ranges[i].lba;
493*8b1d35bdSDavid Sebek 		uint64 length = ranges[i].size;
494*8b1d35bdSDavid Sebek 
495*8b1d35bdSDavid Sebek 		if (length == 0)
496*8b1d35bdSDavid Sebek 			continue; // Length of 0 would be ignored by the device anyway
497*8b1d35bdSDavid Sebek 
498*8b1d35bdSDavid Sebek 		if (lba > UNMAP_MAX_LBA_VALUE) {
499*8b1d35bdSDavid Sebek 			SHOW_ERROR0(1, "LBA value is too large!"
500*8b1d35bdSDavid Sebek 				" This unmap range will be skipped.");
501*8b1d35bdSDavid Sebek 			continue;
50299086aa3SAxel Dörfler 		}
50399086aa3SAxel Dörfler 
504*8b1d35bdSDavid Sebek 		// Split large ranges if needed.
505*8b1d35bdSDavid Sebek 		// Range length is limited by:
506*8b1d35bdSDavid Sebek 		//   - the UNMAP_MAX_BLOCK_COUNT_VALUE constant
507*8b1d35bdSDavid Sebek 		//   - the total number of LBAs in one UNMAP command is limited by
508*8b1d35bdSDavid Sebek 		//     the MAX UNMAP LBA COUNT field in the Block Limits VPD page
509*8b1d35bdSDavid Sebek 		while (length > 0) {
510*8b1d35bdSDavid Sebek 			uint64 trimLength = min_c(length, maxLength);
511*8b1d35bdSDavid Sebek 			trimLength = min_c(trimLength,
512*8b1d35bdSDavid Sebek 					maxBlocksInRequest - trimmedBlocksInRequest);
513*8b1d35bdSDavid Sebek 			unmapList->blocks[descriptorIndex].lba
514*8b1d35bdSDavid Sebek 				= B_HOST_TO_BENDIAN_INT64(lba);
515*8b1d35bdSDavid Sebek 			unmapList->blocks[descriptorIndex].block_count
516*8b1d35bdSDavid Sebek 				= B_HOST_TO_BENDIAN_INT32(trimLength);
517*8b1d35bdSDavid Sebek 			descriptorIndex++;
518*8b1d35bdSDavid Sebek 			trimmedBlocksInRequest += trimLength;
519*8b1d35bdSDavid Sebek 
520*8b1d35bdSDavid Sebek 			// Split into multiple requests if needed.
521*8b1d35bdSDavid Sebek 			// The number of UNMAP block descriptors is limited by:
522*8b1d35bdSDavid Sebek 			//   - the number of block descriptors cannot exceed the
523*8b1d35bdSDavid Sebek 			//     MAXIMUM UNMAP PARAMETER COUNT value in the Block Limits VPD
524*8b1d35bdSDavid Sebek 			//   - the size of our buffer
525*8b1d35bdSDavid Sebek 			//   - what fits in one UNMAP command
526*8b1d35bdSDavid Sebek 			//   - the total number of LBAs in one UNMAP command is limited by
527*8b1d35bdSDavid Sebek 			//     the MAX UNMAP LBA COUNT field in the Block Limits VPD page
528*8b1d35bdSDavid Sebek 			if (descriptorIndex >= maxDescriptors
529*8b1d35bdSDavid Sebek 				|| descriptorIndex >= expectedDescriptorCount
530*8b1d35bdSDavid Sebek 				|| descriptorIndex >= UNMAP_MAX_DESCRIPTORS
531*8b1d35bdSDavid Sebek 				|| trimmedBlocksInRequest >= maxBlocksInRequest
532*8b1d35bdSDavid Sebek 				|| (i == rangeCount - 1 && length <= maxLength))
533*8b1d35bdSDavid Sebek 			{
534*8b1d35bdSDavid Sebek 				uint16 unmapListSize = (descriptorIndex - 1)
535*8b1d35bdSDavid Sebek 						* sizeof(scsi_unmap_block_descriptor)
536*8b1d35bdSDavid Sebek 					+ sizeof(scsi_unmap_parameter_list);
537*8b1d35bdSDavid Sebek 				unmapList->data_length = B_HOST_TO_BENDIAN_INT16(unmapListSize
538*8b1d35bdSDavid Sebek 					- offsetof(scsi_unmap_parameter_list, block_data_length));
539*8b1d35bdSDavid Sebek 				unmapList->block_data_length
540*8b1d35bdSDavid Sebek 					= B_HOST_TO_BENDIAN_INT16(unmapListSize
541*8b1d35bdSDavid Sebek 						- offsetof(scsi_unmap_parameter_list, blocks));
542960c56aeSAxel Dörfler 
54399086aa3SAxel Dörfler 				scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb;
544960c56aeSAxel Dörfler 				memset(cmd, 0, sizeof(*cmd));
54599086aa3SAxel Dörfler 				cmd->opcode = SCSI_OP_UNMAP;
546*8b1d35bdSDavid Sebek 				cmd->length = B_HOST_TO_BENDIAN_INT16(unmapListSize);
54799086aa3SAxel Dörfler 
548*8b1d35bdSDavid Sebek 				request->flags = SCSI_DIR_OUT;
549960c56aeSAxel Dörfler 				request->cdb_length = sizeof(*cmd);
550*8b1d35bdSDavid Sebek 				request->sort = B_BENDIAN_TO_HOST_INT64(
551*8b1d35bdSDavid Sebek 					unmapList->blocks[0].lba);
552*8b1d35bdSDavid Sebek 				request->timeout = device->std_timeout;
553960c56aeSAxel Dörfler 
554*8b1d35bdSDavid Sebek 				request->data = (uint8*)unmapList;
555*8b1d35bdSDavid Sebek 				request->data_length = unmapListSize;
556*8b1d35bdSDavid Sebek 				request->sg_list = NULL;
557*8b1d35bdSDavid Sebek 
558*8b1d35bdSDavid Sebek 				SHOW_FLOW(3, "UNMAP data used %" B_PRIu16
559*8b1d35bdSDavid Sebek 					" of %" B_PRIuSIZE " allocated bytes",
560*8b1d35bdSDavid Sebek 					unmapListSize, unmapListAllocatedSize);
561*8b1d35bdSDavid Sebek 
562*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
563*8b1d35bdSDavid Sebek 				uint16 scsiRangeCount = (uint16)B_BENDIAN_TO_HOST_INT16(
564*8b1d35bdSDavid Sebek 					unmapList->block_data_length)
565*8b1d35bdSDavid Sebek 					/ sizeof(scsi_unmap_block_descriptor);
566*8b1d35bdSDavid Sebek 				uint64 count = 0;
567*8b1d35bdSDavid Sebek 				dprintf("TRIM: SCSI: sending an UNMAP command to"
568*8b1d35bdSDavid Sebek 					" the device (blocks):\n");
569*8b1d35bdSDavid Sebek 				for (uint16 i = 0; i < scsiRangeCount; i++) {
570*8b1d35bdSDavid Sebek 					dprintf("[%3" B_PRIu16 "] %" B_PRIu64 " : %" B_PRIu32 "\n",
571*8b1d35bdSDavid Sebek 						i, (uint64)B_BENDIAN_TO_HOST_INT64(
572*8b1d35bdSDavid Sebek 							unmapList->blocks[i].lba),
573*8b1d35bdSDavid Sebek 						(uint32)B_BENDIAN_TO_HOST_INT32(
574*8b1d35bdSDavid Sebek 							unmapList->blocks[i].block_count));
575*8b1d35bdSDavid Sebek 					count += (uint32)B_BENDIAN_TO_HOST_INT32(
576*8b1d35bdSDavid Sebek 							unmapList->blocks[i].block_count);
577*8b1d35bdSDavid Sebek 				}
578*8b1d35bdSDavid Sebek 				if (device->max_unmap_lba_count >= count)
579*8b1d35bdSDavid Sebek 					dprintf("TRIM: SCSI: Previous UNMAP command would fit %"
580*8b1d35bdSDavid Sebek 						B_PRIu64 " more LBAs\n",
581*8b1d35bdSDavid Sebek 						device->max_unmap_lba_count - count);
582*8b1d35bdSDavid Sebek 				else
583*8b1d35bdSDavid Sebek 					dprintf("TRIM: SCSI: Previous UNMAP ranges exceed the"
584*8b1d35bdSDavid Sebek 						" device limit!\n");
585*8b1d35bdSDavid Sebek #endif /* DEBUG_TRIM */
586*8b1d35bdSDavid Sebek 
587*8b1d35bdSDavid Sebek 				status = periph_safe_exec(device, request);
588960c56aeSAxel Dörfler 
589960c56aeSAxel Dörfler 				// peripheral layer only creates "read" error
590e6bde50aSAxel Dörfler 				if (status == B_DEV_READ_ERROR)
591960c56aeSAxel Dörfler 					return B_DEV_WRITE_ERROR;
592*8b1d35bdSDavid Sebek 				else if (status != B_OK)
593*8b1d35bdSDavid Sebek 					return status;
594*8b1d35bdSDavid Sebek 
595*8b1d35bdSDavid Sebek 				*trimmedBlocks += trimmedBlocksInRequest;
596*8b1d35bdSDavid Sebek 
597*8b1d35bdSDavid Sebek 				descriptorIndex = 0;
598*8b1d35bdSDavid Sebek 				trimmedBlocksInRequest = 0;
599*8b1d35bdSDavid Sebek 				memset(unmapList, 0, unmapListSize);
600*8b1d35bdSDavid Sebek 			}
601*8b1d35bdSDavid Sebek 
602*8b1d35bdSDavid Sebek 			length -= trimLength;
603*8b1d35bdSDavid Sebek 			lba += trimLength;
604*8b1d35bdSDavid Sebek 		}
605*8b1d35bdSDavid Sebek 	}
606960c56aeSAxel Dörfler 
607e6bde50aSAxel Dörfler 	return status;
608960c56aeSAxel Dörfler }
609960c56aeSAxel Dörfler 
610*8b1d35bdSDavid Sebek 
611*8b1d35bdSDavid Sebek static status_t
612*8b1d35bdSDavid Sebek trim_writesame16(scsi_periph_device_info* device, scsi_ccb* request,
613*8b1d35bdSDavid Sebek 	scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
614*8b1d35bdSDavid Sebek {
615*8b1d35bdSDavid Sebek 	status_t status = B_OK;
616*8b1d35bdSDavid Sebek 	*trimmedBlocks = 0;
617*8b1d35bdSDavid Sebek 
618*8b1d35bdSDavid Sebek 	for (uint32 i = 0; i < rangeCount; i++) {
619*8b1d35bdSDavid Sebek 		uint64 lba = ranges[i].lba;
620*8b1d35bdSDavid Sebek 		uint64 length = ranges[i].size;
621*8b1d35bdSDavid Sebek 
622*8b1d35bdSDavid Sebek 		if (length == 0)
623*8b1d35bdSDavid Sebek 			continue; // length of 0 would mean the rest of the device!
624*8b1d35bdSDavid Sebek 
625*8b1d35bdSDavid Sebek 		if (lba > WS16_MAX_LBA_VALUE) {
626*8b1d35bdSDavid Sebek 			SHOW_ERROR0(1, "LBA value is too large!"
627*8b1d35bdSDavid Sebek 				" This unmap range will be skipped.");
628*8b1d35bdSDavid Sebek 			continue;
629*8b1d35bdSDavid Sebek 		}
630*8b1d35bdSDavid Sebek 
631*8b1d35bdSDavid Sebek 		// Split the range into multiple requests if needed
632*8b1d35bdSDavid Sebek 		uint64 maxLength = min_c(device->max_unmap_lba_count,
633*8b1d35bdSDavid Sebek 				WS16_MAX_BLOCK_COUNT_VALUE);
634*8b1d35bdSDavid Sebek 		while (length > 0) {
635*8b1d35bdSDavid Sebek 			uint64 trimLength = min_c(length, maxLength);
636*8b1d35bdSDavid Sebek 			if (trimLength == 0) {
637*8b1d35bdSDavid Sebek 				SHOW_ERROR0(1,
638*8b1d35bdSDavid Sebek 					"Error: Length of zero in WRITE SAME (16) detected");
639*8b1d35bdSDavid Sebek 				break;
640*8b1d35bdSDavid Sebek 			}
641*8b1d35bdSDavid Sebek 
642*8b1d35bdSDavid Sebek 			void* block = malloc(device->block_size);
643*8b1d35bdSDavid Sebek 			if (block == NULL)
644*8b1d35bdSDavid Sebek 				return B_NO_MEMORY;
645*8b1d35bdSDavid Sebek 			MemoryDeleter deleter(block);
646*8b1d35bdSDavid Sebek 			memset(block, 0, device->block_size);
647*8b1d35bdSDavid Sebek 
648*8b1d35bdSDavid Sebek 			scsi_cmd_wsame_16* cmd = (scsi_cmd_wsame_16*)request->cdb;
649*8b1d35bdSDavid Sebek 			memset(cmd, 0, sizeof(*cmd));
650*8b1d35bdSDavid Sebek 			cmd->opcode = SCSI_OP_WRITE_SAME_16;
651*8b1d35bdSDavid Sebek 			cmd->unmap = 1;
652*8b1d35bdSDavid Sebek 			cmd->lba = B_HOST_TO_BENDIAN_INT64(lba);
653*8b1d35bdSDavid Sebek 			cmd->length = B_HOST_TO_BENDIAN_INT32(trimLength);
654*8b1d35bdSDavid Sebek 			//cmd->ndob = 1; // no data is needed if this bit is enabled
655*8b1d35bdSDavid Sebek 
656*8b1d35bdSDavid Sebek 			request->flags = SCSI_DIR_OUT;
657*8b1d35bdSDavid Sebek 			request->cdb_length = sizeof(*cmd);
658*8b1d35bdSDavid Sebek 			request->sort = lba;
659*8b1d35bdSDavid Sebek 			request->timeout = device->std_timeout;
660*8b1d35bdSDavid Sebek 
661*8b1d35bdSDavid Sebek 			request->data = (uint8*)block;
662*8b1d35bdSDavid Sebek 			request->data_length = device->block_size;
663*8b1d35bdSDavid Sebek 			request->sg_list = NULL;
664*8b1d35bdSDavid Sebek 
665*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
666*8b1d35bdSDavid Sebek 			dprintf("TRIM: SCSI: sending a WRITE SAME (16) command to"
667*8b1d35bdSDavid Sebek 				" the device (blocks):\n");
668*8b1d35bdSDavid Sebek 			dprintf("%" B_PRIu64 " : %" B_PRIu32 "\n",
669*8b1d35bdSDavid Sebek 				(uint64)B_BENDIAN_TO_HOST_INT64(cmd->lba),
670*8b1d35bdSDavid Sebek 				(uint32)B_BENDIAN_TO_HOST_INT32(cmd->length));
671*8b1d35bdSDavid Sebek #endif
672*8b1d35bdSDavid Sebek 
673*8b1d35bdSDavid Sebek 			status = periph_safe_exec(device, request);
674*8b1d35bdSDavid Sebek 
675*8b1d35bdSDavid Sebek 			// peripheral layer only creates "read" error
676*8b1d35bdSDavid Sebek 			if (status == B_DEV_READ_ERROR)
677*8b1d35bdSDavid Sebek 				return B_DEV_WRITE_ERROR;
678*8b1d35bdSDavid Sebek 			else if (status != B_OK)
679*8b1d35bdSDavid Sebek 				return status;
680*8b1d35bdSDavid Sebek 
681*8b1d35bdSDavid Sebek 			*trimmedBlocks += trimLength;
682*8b1d35bdSDavid Sebek 			length -= trimLength;
683*8b1d35bdSDavid Sebek 			lba += trimLength;
684*8b1d35bdSDavid Sebek 		}
685*8b1d35bdSDavid Sebek 	}
686*8b1d35bdSDavid Sebek 
687*8b1d35bdSDavid Sebek 	return status;
688*8b1d35bdSDavid Sebek }
689*8b1d35bdSDavid Sebek 
690*8b1d35bdSDavid Sebek 
691*8b1d35bdSDavid Sebek static status_t
692*8b1d35bdSDavid Sebek trim_writesame10(scsi_periph_device_info* device, scsi_ccb* request,
693*8b1d35bdSDavid Sebek 	scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
694*8b1d35bdSDavid Sebek {
695*8b1d35bdSDavid Sebek 	status_t status = B_OK;
696*8b1d35bdSDavid Sebek 	*trimmedBlocks = 0;
697*8b1d35bdSDavid Sebek 
698*8b1d35bdSDavid Sebek 	for (uint32 i = 0; i < rangeCount; i++) {
699*8b1d35bdSDavid Sebek 		uint64 lba = ranges[i].lba;
700*8b1d35bdSDavid Sebek 		uint64 length = ranges[i].size;
701*8b1d35bdSDavid Sebek 
702*8b1d35bdSDavid Sebek 		if (length == 0)
703*8b1d35bdSDavid Sebek 			continue; // length of 0 would mean the rest of the device!
704*8b1d35bdSDavid Sebek 
705*8b1d35bdSDavid Sebek 		if (lba > WS10_MAX_LBA_VALUE) {
706*8b1d35bdSDavid Sebek 			SHOW_ERROR0(1, "LBA value is too large!"
707*8b1d35bdSDavid Sebek 				" This unmap range will be skipped.");
708*8b1d35bdSDavid Sebek 			continue;
709*8b1d35bdSDavid Sebek 		}
710*8b1d35bdSDavid Sebek 
711*8b1d35bdSDavid Sebek 		// Split the range into multiple requests if needed
712*8b1d35bdSDavid Sebek 		uint64 maxLength = min_c(device->max_unmap_lba_count,
713*8b1d35bdSDavid Sebek 				WS10_MAX_BLOCK_COUNT_VALUE);
714*8b1d35bdSDavid Sebek 		while (length > 0) {
715*8b1d35bdSDavid Sebek 			uint64 trimLength = min_c(length, maxLength);
716*8b1d35bdSDavid Sebek 			if (trimLength == 0) {
717*8b1d35bdSDavid Sebek 				SHOW_ERROR0(1,
718*8b1d35bdSDavid Sebek 					"Error: Length of zero in WRITE SAME (10) detected");
719*8b1d35bdSDavid Sebek 				break;
720*8b1d35bdSDavid Sebek 			}
721*8b1d35bdSDavid Sebek 
722*8b1d35bdSDavid Sebek 			void* block = malloc(device->block_size);
723*8b1d35bdSDavid Sebek 			if (block == NULL)
724*8b1d35bdSDavid Sebek 				return B_NO_MEMORY;
725*8b1d35bdSDavid Sebek 			MemoryDeleter deleter(block);
726*8b1d35bdSDavid Sebek 			memset(block, 0, device->block_size);
727*8b1d35bdSDavid Sebek 
728*8b1d35bdSDavid Sebek 			scsi_cmd_wsame_10* cmd = (scsi_cmd_wsame_10*)request->cdb;
729*8b1d35bdSDavid Sebek 			memset(cmd, 0, sizeof(*cmd));
730*8b1d35bdSDavid Sebek 			cmd->opcode = SCSI_OP_WRITE_SAME_10;
731*8b1d35bdSDavid Sebek 			cmd->unmap = 1;
732*8b1d35bdSDavid Sebek 			cmd->lba = B_HOST_TO_BENDIAN_INT32(lba);
733*8b1d35bdSDavid Sebek 			cmd->length = B_HOST_TO_BENDIAN_INT16(trimLength);
734*8b1d35bdSDavid Sebek 
735*8b1d35bdSDavid Sebek 			request->flags = SCSI_DIR_OUT;
736*8b1d35bdSDavid Sebek 			request->cdb_length = sizeof(*cmd);
737*8b1d35bdSDavid Sebek 			request->sort = lba;
738*8b1d35bdSDavid Sebek 			request->timeout = device->std_timeout;
739*8b1d35bdSDavid Sebek 
740*8b1d35bdSDavid Sebek 			request->data = (uint8*)block;
741*8b1d35bdSDavid Sebek 			request->data_length = device->block_size;
742*8b1d35bdSDavid Sebek 			request->sg_list = NULL;
743*8b1d35bdSDavid Sebek 
744*8b1d35bdSDavid Sebek #ifdef DEBUG_TRIM
745*8b1d35bdSDavid Sebek 			dprintf("TRIM: SCSI: sending a WRITE SAME (10) command to"
746*8b1d35bdSDavid Sebek 				" the device (blocks):\n");
747*8b1d35bdSDavid Sebek 			dprintf("%" B_PRIu32 " : %" B_PRIu16 "\n",
748*8b1d35bdSDavid Sebek 				(uint32)B_BENDIAN_TO_HOST_INT32(cmd->lba),
749*8b1d35bdSDavid Sebek 				(uint16)B_BENDIAN_TO_HOST_INT16(cmd->length));
750*8b1d35bdSDavid Sebek #endif
751*8b1d35bdSDavid Sebek 
752*8b1d35bdSDavid Sebek 			status = periph_safe_exec(device, request);
753*8b1d35bdSDavid Sebek 
754*8b1d35bdSDavid Sebek 			// peripheral layer only creates "read" error
755*8b1d35bdSDavid Sebek 			if (status == B_DEV_READ_ERROR)
756*8b1d35bdSDavid Sebek 				return B_DEV_WRITE_ERROR;
757*8b1d35bdSDavid Sebek 			else if (status != B_OK)
758*8b1d35bdSDavid Sebek 				return status;
759*8b1d35bdSDavid Sebek 
760*8b1d35bdSDavid Sebek 			*trimmedBlocks += trimLength;
761*8b1d35bdSDavid Sebek 			length -= trimLength;
762*8b1d35bdSDavid Sebek 			lba += trimLength;
763*8b1d35bdSDavid Sebek 		}
764*8b1d35bdSDavid Sebek 	}
765*8b1d35bdSDavid Sebek 
766*8b1d35bdSDavid Sebek 	return status;
767*8b1d35bdSDavid Sebek }
768*8b1d35bdSDavid Sebek 
769*8b1d35bdSDavid Sebek 
770*8b1d35bdSDavid Sebek status_t
771*8b1d35bdSDavid Sebek periph_trim_device(scsi_periph_device_info* device, scsi_ccb* request,
772*8b1d35bdSDavid Sebek 	scsi_block_range* ranges, uint32 rangeCount, uint64* trimmedBlocks)
773*8b1d35bdSDavid Sebek {
774*8b1d35bdSDavid Sebek 	*trimmedBlocks = 0;
775*8b1d35bdSDavid Sebek 
776*8b1d35bdSDavid Sebek 	if (device->unmap_command == TRIM_NONE
777*8b1d35bdSDavid Sebek 		|| device->max_unmap_lba_count == 0
778*8b1d35bdSDavid Sebek 		|| device->max_unmap_descriptor_count == 0)
779*8b1d35bdSDavid Sebek 		return B_UNSUPPORTED;
780*8b1d35bdSDavid Sebek 
781*8b1d35bdSDavid Sebek 	switch (device->unmap_command) {
782*8b1d35bdSDavid Sebek 		case TRIM_UNMAP:
783*8b1d35bdSDavid Sebek 			return trim_unmap(device, request, ranges, rangeCount,
784*8b1d35bdSDavid Sebek 				trimmedBlocks);
785*8b1d35bdSDavid Sebek 		case TRIM_WRITESAME16:
786*8b1d35bdSDavid Sebek 			return trim_writesame16(device, request, ranges, rangeCount,
787*8b1d35bdSDavid Sebek 				trimmedBlocks);
788*8b1d35bdSDavid Sebek 		case TRIM_WRITESAME10:
789*8b1d35bdSDavid Sebek 			return trim_writesame10(device, request, ranges, rangeCount,
790*8b1d35bdSDavid Sebek 				trimmedBlocks);
791*8b1d35bdSDavid Sebek 		default:
792*8b1d35bdSDavid Sebek 			return B_UNSUPPORTED;
793*8b1d35bdSDavid Sebek 	}
794*8b1d35bdSDavid Sebek }
795