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
19status_t
20periph_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
121status_t
122periph_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