btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / generic / scsi_periph / block.cpp
blob000726e7d9d4a32b926ff8fc268e9ad91f795fac
1 /*
2 * Copyright 2004-2013, Haiku, Inc. All RightsReserved.
3 * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 //! Handling of block device
12 #include <string.h>
14 #include <AutoDeleter.h>
16 #include "scsi_periph_int.h"
19 status_t
20 periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request)
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;
28 SHOW_FLOW(3, "%p, %p", device, request);
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;
35 request->flags = SCSI_DIR_IN;
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;
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
49 res = periph_safe_exec(device, request);
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;
58 mutex_lock(&device->mutex);
60 if (res == B_OK && request->data_resid == 0) {
61 capacity = B_BENDIAN_TO_HOST_INT32(capacityResult.lba);
63 if (capacity == UINT_MAX) {
64 mutex_unlock(&device->mutex);
66 scsi_cmd_read_capacity_long *cmd
67 = (scsi_cmd_read_capacity_long *)request->cdb;
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);
74 memset(cmd, 0, sizeof(*cmd));
75 cmd->opcode = SCSI_OP_SERVICE_ACTION_IN;
76 cmd->service_action = SCSI_SAI_READ_CAPACITY_16;
78 res = periph_safe_exec(device, request);
80 mutex_lock(&device->mutex);
82 if (res == B_OK && request->data_resid == 0) {
83 capacity = B_BENDIAN_TO_HOST_INT64(capacityLongResult.lba);
84 } else
85 capacity = 0;
88 // the command returns the index of the _last_ block,
89 // i.e. the size is one larger
90 ++capacity;
92 blockSize = B_BENDIAN_TO_HOST_INT32(capacityResult.block_size);
93 } else {
94 capacity = 0;
95 blockSize = 0;
98 SHOW_FLOW(3, "capacity = %" B_PRId64 ", block_size = %" B_PRId32, capacity,
99 blockSize);
101 device->block_size = blockSize;
103 device->callbacks->set_capacity(device->periph_device,
104 capacity, blockSize);
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;
113 mutex_unlock(&device->mutex);
115 SHOW_FLOW(3, "done (%s)", strerror(res));
117 return res;
121 status_t
122 periph_trim_device(scsi_periph_device_info *device, scsi_ccb *request,
123 scsi_block_range* ranges, uint32 rangeCount)
125 size_t unmapBlockSize = (rangeCount - 1)
126 * sizeof(scsi_unmap_block_descriptor)
127 + sizeof(scsi_unmap_parameter_list);
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;
135 scsi_unmap_parameter_list* unmapBlocks
136 = (scsi_unmap_parameter_list*)malloc(unmapBlockSize);
137 if (unmapBlocks == NULL)
138 return B_NO_MEMORY;
140 MemoryDeleter deleter(unmapBlocks);
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);
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);
155 request->flags = SCSI_DIR_OUT;
156 request->sort = ranges[0].offset / device->block_size;
157 request->timeout = device->std_timeout;
159 scsi_cmd_unmap* cmd = (scsi_cmd_unmap*)request->cdb;
161 memset(cmd, 0, sizeof(*cmd));
162 cmd->opcode = SCSI_OP_UNMAP;
163 cmd->length = B_HOST_TO_BENDIAN_INT16(unmapBlockSize);
165 request->data = (uint8*)unmapBlocks;
166 request->data_length = unmapBlockSize;
168 request->cdb_length = sizeof(*cmd);
170 status_t status = periph_safe_exec(device, request);
172 // peripheral layer only creates "read" error
173 if (status == B_DEV_READ_ERROR)
174 return B_DEV_WRITE_ERROR;
176 return status;