xref: /haiku/src/add-ons/kernel/generic/scsi_periph/block.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2004-2013, Haiku, Inc. All RightsReserved.
3  * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 //!	Handling of block device
10 
11 
12 #include <string.h>
13 
14 #include <AutoDeleter.h>
15 
16 #include "scsi_periph_int.h"
17 
18 
19 status_t
20 periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request)
21 {
22 	scsi_res_read_capacity capacityResult;
23 	scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb;
24 	uint64 capacity;
25 	uint32 blockSize;
26 	status_t res;
27 
28 	SHOW_FLOW(3, "%p, %p", device, request);
29 
30 	// driver doesn't support capacity callback - seems to be no block
31 	// device driver, so ignore
32 	if (device->callbacks->set_capacity == NULL)
33 		return B_OK;
34 
35 	request->flags = SCSI_DIR_IN;
36 
37 	request->data = (uint8*)&capacityResult;
38 	request->data_length = sizeof(capacityResult);
39 	request->cdb_length = sizeof(scsi_cmd_read_capacity);
40 	request->timeout = device->std_timeout;
41 	request->sort = -1;
42 	request->sg_list = NULL;
43 
44 	memset(cmd, 0, sizeof(*cmd));
45 	cmd->opcode = SCSI_OP_READ_CAPACITY;
46 	// we don't set PMI (partial medium indicator) as we want the whole capacity;
47 	// in this case, all other parameters must be zero
48 
49 	res = periph_safe_exec(device, request);
50 
51 	if (res == B_DEV_MEDIA_CHANGED) {
52 		// in this case, the error handler has already called check_capacity
53 		// recursively, so we ignore our (invalid) result
54 		SHOW_FLOW0( 3, "ignore result because medium change" );
55 		return B_DEV_MEDIA_CHANGED;
56 	}
57 
58 	mutex_lock(&device->mutex);
59 
60 	if (res == B_OK && request->data_resid == 0) {
61 		capacity = B_BENDIAN_TO_HOST_INT32(capacityResult.lba);
62 
63 		if (capacity == UINT_MAX) {
64 			mutex_unlock(&device->mutex);
65 
66 			scsi_cmd_read_capacity_long *cmd
67 				= (scsi_cmd_read_capacity_long *)request->cdb;
68 
69 			scsi_res_read_capacity_long capacityLongResult;
70 			request->data = (uint8*)&capacityLongResult;
71 			request->data_length = sizeof(capacityLongResult);
72 			request->cdb_length = sizeof(scsi_cmd_read_capacity_long);
73 
74 			memset(cmd, 0, sizeof(*cmd));
75 			cmd->opcode = SCSI_OP_SERVICE_ACTION_IN;
76 			cmd->service_action = SCSI_SAI_READ_CAPACITY_16;
77 
78 			res = periph_safe_exec(device, request);
79 
80 			mutex_lock(&device->mutex);
81 
82 			if (res == B_OK && request->data_resid == 0) {
83 				capacity = B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba);
84 			} else
85 				capacity = 0;
86 		}
87 
88 		// the command returns the index of the _last_ block,
89 		// i.e. the size is one larger
90 		++capacity;
91 
92 		blockSize = B_BENDIAN_TO_HOST_INT32(capacityResult.block_size);
93 	} else {
94 		capacity = 0;
95 		blockSize = 0;
96 	}
97 
98 	SHOW_FLOW(3, "capacity = %" B_PRId64 ", block_size = %" B_PRId32, capacity,
99 		blockSize);
100 
101 	device->block_size = blockSize;
102 
103 	device->callbacks->set_capacity(device->periph_device,
104 		capacity, blockSize);
105 
106 /*	device->byte2blk_shift = log2( device->block_size );
107 	if( device->byte2blk_shift < 0 ) {
108 		// this may be too restrictive...
109 		device->capacity = -1;
110 		return ERR_DEV_GENERAL;
111 	}*/
112 
113 	mutex_unlock(&device->mutex);
114 
115 	SHOW_FLOW(3, "done (%s)", strerror(res));
116 
117 	return res;
118 }
119 
120 
121 status_t
122 periph_trim_device(scsi_periph_device_info *device, scsi_ccb *request,
123 	scsi_block_range* ranges, uint32 rangeCount)
124 {
125 	size_t unmapBlockSize = (rangeCount - 1)
126 			* sizeof(scsi_unmap_block_descriptor)
127 		+ sizeof(scsi_unmap_parameter_list);
128 
129 	// TODO: check block limits VPD page
130 	// TODO: instead of failing, we should try to complete the request in
131 	// several passes.
132 	if (unmapBlockSize > 65536 || rangeCount == 0)
133 		return B_BAD_VALUE;
134 
135 	scsi_unmap_parameter_list* unmapBlocks
136 		= (scsi_unmap_parameter_list*)malloc(unmapBlockSize);
137 	if (unmapBlocks == NULL)
138 		return B_NO_MEMORY;
139 
140 	MemoryDeleter deleter(unmapBlocks);
141 
142 	// Prepare request data
143 	memset(unmapBlocks, 0, unmapBlockSize);
144 	unmapBlocks->data_length = B_HOST_TO_BENDIAN_INT16(unmapBlockSize - 1);
145 	unmapBlocks->block_data_length
146 		= B_HOST_TO_BENDIAN_INT16(unmapBlockSize - 7);
147 
148 	for (uint32 i = 0; i < rangeCount; i++) {
149 		unmapBlocks->blocks[i].lba = B_HOST_TO_BENDIAN_INT64(
150 			ranges[i].offset / device->block_size);
151 		unmapBlocks->blocks[i].block_count = B_HOST_TO_BENDIAN_INT32(
152 			ranges[i].size / device->block_size);
153 	}
154 
155 	request->flags = SCSI_DIR_OUT;
156 	request->sort = ranges[0].offset / device->block_size;
157 	request->timeout = device->std_timeout;
158 
159 	scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb;
160 
161 	memset(cmd, 0, sizeof(*cmd));
162 	cmd->opcode = SCSI_OP_UNMAP;
163 	cmd->length = B_HOST_TO_BENDIAN_INT16(unmapBlockSize);
164 
165 	request->data = (uint8*)unmapBlocks;
166 	request->data_length = unmapBlockSize;
167 
168 	request->cdb_length = sizeof(*cmd);
169 
170 	status_t status = periph_safe_exec(device, request);
171 
172 	// peripheral layer only creates "read" error
173 	if (status == B_DEV_READ_ERROR)
174 		return B_DEV_WRITE_ERROR;
175 
176 	return status;
177 }
178 
179