1 /* 2 * Copyright 2004-2010, Haiku, Inc. All Rights Reserved. 3 * Copyright 2002/03, Thomas Kurschel. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 //! Everything doing the real input/output stuff. 10 11 12 #include "scsi_periph_int.h" 13 #include <scsi.h> 14 15 #include <string.h> 16 #include <stdlib.h> 17 18 19 static status_t 20 inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry) 21 { 22 const scsi_res_inquiry *device_inquiry = NULL; 23 size_t inquiryLength; 24 25 if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, 26 (const void **)&device_inquiry, &inquiryLength, true) != B_OK) 27 return B_ERROR; 28 29 memcpy(inquiry, device_inquiry, min_c(inquiryLength, sizeof(scsi_inquiry))); 30 return B_OK; 31 } 32 33 34 static status_t 35 prevent_allow(scsi_periph_device_info *device, bool prevent) 36 { 37 scsi_cmd_prevent_allow cmd; 38 39 SHOW_FLOW0(0, ""); 40 41 memset(&cmd, 0, sizeof(cmd)); 42 cmd.opcode = SCSI_OP_PREVENT_ALLOW; 43 cmd.prevent = prevent; 44 45 return periph_simple_exec(device, (uint8 *)&cmd, sizeof(cmd), NULL, 0, 46 SCSI_DIR_NONE); 47 } 48 49 50 /*! Keep this in sync with scsi_raw driver!!! */ 51 static status_t 52 raw_command(scsi_periph_device_info *device, raw_device_command *cmd) 53 { 54 scsi_ccb *request; 55 56 SHOW_FLOW0(0, ""); 57 58 request = device->scsi->alloc_ccb(device->scsi_device); 59 if (request == NULL) 60 return B_NO_MEMORY; 61 62 request->flags = 0; 63 64 if (cmd->flags & B_RAW_DEVICE_DATA_IN) 65 request->flags |= SCSI_DIR_IN; 66 else if (cmd->data_length) 67 request->flags |= SCSI_DIR_OUT; 68 else 69 request->flags |= SCSI_DIR_NONE; 70 71 request->data = (uint8*)cmd->data; 72 request->sg_list = NULL; 73 request->data_length = cmd->data_length; 74 request->sort = -1; 75 request->timeout = cmd->timeout; 76 77 memcpy(request->cdb, cmd->command, SCSI_MAX_CDB_SIZE); 78 request->cdb_length = cmd->command_length; 79 80 device->scsi->sync_io(request); 81 82 // TBD: should we call standard error handler here, or may the 83 // actions done there (like starting the unit) confuse the application? 84 85 cmd->cam_status = request->subsys_status; 86 cmd->scsi_status = request->device_status; 87 88 if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) { 89 memcpy(cmd->sense_data, request->sense, min_c(cmd->sense_data_length, 90 (size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid)); 91 } 92 93 if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) { 94 // this is a bit strange, see Be's sample code where I pinched this from; 95 // normally, residual means "number of unused bytes left" 96 // but here, we have to return "number of used bytes", which is the opposite 97 cmd->data_length = cmd->data_length - request->data_resid; 98 cmd->sense_data_length = SCSI_MAX_SENSE_SIZE - request->sense_resid; 99 } 100 101 device->scsi->free_ccb(request); 102 103 return B_OK; 104 } 105 106 107 /*! Universal read/write function */ 108 static status_t 109 read_write(scsi_periph_device_info *device, scsi_ccb *request, 110 io_operation *operation, uint64 offset, size_t originalNumBlocks, 111 physical_entry* vecs, size_t vecCount, bool isWrite, 112 size_t* _bytesTransferred) 113 { 114 uint32 blockSize = device->block_size; 115 size_t numBlocks = originalNumBlocks; 116 uint32 pos = offset; 117 err_res res; 118 int retries = 0; 119 120 do { 121 size_t numBytes; 122 bool isReadWrite10; 123 124 request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN; 125 126 // io_operations are generated by a DMAResource and thus contain DMA 127 // safe physical vectors 128 if (operation != NULL) 129 request->flags |= SCSI_DMA_SAFE; 130 131 // make sure we avoid 10 byte commands if they aren't supported 132 if (!device->rw10_enabled || device->preferred_ccb_size == 6) { 133 // restricting transfer is OK - the block manager will 134 // take care of transferring the rest 135 if (numBlocks > 0x100) 136 numBlocks = 0x100; 137 138 // no way to break the 21 bit address limit 139 if (pos > 0x200000) 140 return B_BAD_VALUE; 141 142 // don't allow transfer cross the 24 bit address limit 143 // (I'm not sure whether this is allowed, but this way we 144 // are sure to not ask for trouble) 145 if (pos < 0x100000) 146 numBlocks = min_c(numBlocks, 0x100000 - pos); 147 } 148 149 numBytes = numBlocks * blockSize; 150 if (numBlocks != originalNumBlocks) 151 panic("I/O operation would need to be cut."); 152 153 request->data = NULL; 154 request->sg_list = vecs; 155 request->data_length = numBytes; 156 request->sg_count = vecCount; 157 request->io_operation = operation; 158 request->sort = pos; 159 request->timeout = device->std_timeout; 160 // see whether daemon instructed us to post an ordered command; 161 // reset flag after read 162 SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s", 163 (int)request->flags, (int)device->next_tag_action, 164 (request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no"); 165 166 // use shortest commands whenever possible 167 if (pos + numBlocks < 0x200000 && numBlocks <= 0x100) { 168 scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb; 169 170 isReadWrite10 = false; 171 172 memset(cmd, 0, sizeof(*cmd)); 173 cmd->opcode = isWrite ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6; 174 cmd->high_lba = (pos >> 16) & 0x1f; 175 cmd->mid_lba = (pos >> 8) & 0xff; 176 cmd->low_lba = pos & 0xff; 177 cmd->length = numBlocks; 178 179 request->cdb_length = sizeof(*cmd); 180 } else { 181 scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb; 182 183 isReadWrite10 = true; 184 185 memset(cmd, 0, sizeof(*cmd)); 186 cmd->opcode = isWrite ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10; 187 cmd->relative_address = 0; 188 cmd->force_unit_access = 0; 189 cmd->disable_page_out = 0; 190 cmd->lba = B_HOST_TO_BENDIAN_INT32(pos); 191 cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks); 192 193 request->cdb_length = sizeof(*cmd); 194 } 195 196 // TODO: last chance to detect errors that occured during concurrent accesses 197 //status_t status = handle->pending_error; 198 //if (status != B_OK) 199 // return status; 200 201 device->scsi->async_io(request); 202 203 acquire_sem(request->completion_sem); 204 205 // ask generic peripheral layer what to do now 206 res = periph_check_error(device, request); 207 208 // TODO: bytes might have been transferred even in the error case! 209 switch (res.action) { 210 case err_act_ok: 211 *_bytesTransferred = numBytes - request->data_resid; 212 break; 213 214 case err_act_start: 215 res = periph_send_start_stop(device, request, 1, 216 device->removable); 217 if (res.action == err_act_ok) 218 res.action = err_act_retry; 219 break; 220 221 case err_act_invalid_req: 222 // if this was a 10 byte command, the device probably doesn't 223 // support them, so disable them and retry 224 if (isReadWrite10) { 225 atomic_and(&device->rw10_enabled, 0); 226 res.action = err_act_retry; 227 } else 228 res.action = err_act_fail; 229 break; 230 } 231 } while ((res.action == err_act_retry && retries++ < 3) 232 || (res.action == err_act_many_retries && retries++ < 30)); 233 234 // peripheral layer only created "read" error, so we have to 235 // map them to "write" errors if this was a write request 236 if (res.error_code == B_DEV_READ_ERROR && isWrite) 237 return B_DEV_WRITE_ERROR; 238 239 return res.error_code; 240 } 241 242 243 // #pragma mark - public functions 244 245 246 status_t 247 periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer, 248 size_t length) 249 { 250 switch (op) { 251 case B_GET_MEDIA_STATUS: { 252 status_t res = B_OK; 253 254 if (handle->device->removable) 255 res = periph_get_media_status(handle); 256 257 SHOW_FLOW(2, "%s", strerror(res)); 258 259 *(status_t *)buffer = res; 260 return B_OK; 261 } 262 263 case B_SCSI_INQUIRY: 264 return inquiry(handle->device, (scsi_inquiry *)buffer); 265 266 case B_SCSI_PREVENT_ALLOW: 267 return prevent_allow(handle->device, *(bool *)buffer); 268 269 case B_RAW_DEVICE_COMMAND: 270 return raw_command(handle->device, (raw_device_command*)buffer); 271 272 default: 273 if (handle->device->scsi->ioctl != NULL) { 274 return handle->device->scsi->ioctl(handle->device->scsi_device, 275 op, buffer, length); 276 } 277 278 SHOW_ERROR(4, "Unknown ioctl: %x", op); 279 return B_BAD_VALUE; 280 } 281 } 282 283 284 /*! Kernel daemon - once in a minute, it sets a flag so that the next command 285 is executed ordered; this way, we avoid starvation of SCSI commands inside 286 the SCSI queuing system - the ordered command waits for all previous 287 commands and thus no command can starve longer then a minute 288 */ 289 void 290 periph_sync_queue_daemon(void *arg, int iteration) 291 { 292 scsi_periph_device_info *device = (scsi_periph_device_info *)arg; 293 294 SHOW_FLOW0(3, "Setting ordered flag for next R/W access"); 295 atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG); 296 } 297 298 299 status_t 300 periph_read_write(scsi_periph_device_info *device, scsi_ccb *request, 301 uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount, 302 bool isWrite, size_t* _bytesTransferred) 303 { 304 return read_write(device, request, NULL, offset, numBlocks, vecs, vecCount, 305 isWrite, _bytesTransferred); 306 } 307 308 309 status_t 310 periph_io(scsi_periph_device_info *device, io_operation *operation, 311 size_t* _bytesTransferred) 312 { 313 const uint32 blockSize = device->block_size; 314 315 // don't test rw10_enabled restrictions - this flag may get changed 316 scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device); 317 if (request == NULL) 318 return B_NO_MEMORY; 319 320 status_t status = read_write(device, request, operation, 321 operation->Offset() / blockSize, operation->Length() / blockSize, 322 (physical_entry *)operation->Vecs(), operation->VecCount(), 323 operation->IsWrite(), _bytesTransferred); 324 325 device->scsi->free_ccb(request); 326 return status; 327 } 328 329