/*! Send START/STOP command to device start - true for start, false for stop withLoadEject - if true, then lock drive on start and eject on stop */ err_res periph_send_start_stop(scsi_periph_device_info *device, scsi_ccb *request, bool start, bool withLoadEject) { scsi_cmd_ssu *cmd = (scsi_cmd_ssu *)request->cdb; // this must be ordered, so all previous commands are really finished request->flags = SCSI_DIR_NONE | SCSI_ORDERED_QTAG; request->data = NULL; request->sg_list = NULL; request->data_length = 0; request->timeout = device->std_timeout; request->sort = -1; request->sg_list = NULL; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = SCSI_OP_START_STOP; // we don't want to poll; we give a long timeout instead // (well - the default timeout _is_ large) cmd->immediately = 0; cmd->start = start; cmd->load_eject = withLoadEject; request->cdb_length = sizeof(*cmd); device->scsi->sync_io(request); return periph_check_error(device, request); }
err_res periph_synchronize_cache(scsi_periph_device_info *device, scsi_ccb *request) { scsi_cmd_sync_cache* cmd = (scsi_cmd_sync_cache*)request->cdb; request->flags = SCSI_DIR_NONE; request->data = NULL; request->sg_list = NULL; request->data_length = 0; request->timeout = device->std_timeout; request->sort = -1; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = SCSI_OP_SYNCHRONIZE_CACHE; cmd->immediately = 0; // TODO: Maybe we will actually want to set this one day... cmd->block_count = 0; request->cdb_length = sizeof(*cmd); device->scsi->sync_io(request); return periph_check_error(device, request); }
status_t periph_safe_exec(scsi_periph_device_info *device, scsi_ccb *request) { err_res res; int retries = 0; do { device->scsi->sync_io(request); // ask generic peripheral layer what to do now res = periph_check_error(device, request); if (res.action == err_act_start) { // backup request, as we need it temporarily for sending "start" // (we cannot allocate a new cdb as there may be no more cdb and // waiting for one to become empty may lead to deadlock if everyone // does that) uint32 backup_flags; uint8 backup_cdb[SCSI_MAX_CDB_SIZE]; uchar backup_cdb_len; int64 backup_sort; bigtime_t backup_timeout; uchar *backup_data; const physical_entry *backup_sg_list; uint16 backup_sg_count; uint32 backup_data_len; backup_flags = request->flags; memcpy(backup_cdb, request->cdb, SCSI_MAX_CDB_SIZE); backup_cdb_len = request->cdb_length; backup_sort = request->sort; backup_timeout = request->timeout; backup_data = request->data; backup_sg_list = request->sg_list; backup_sg_count = request->sg_count; backup_data_len = request->data_length; SHOW_INFO0( 2, "Sending start to init LUN" ); res = periph_send_start_stop(device, request, 1, device->removable); request->flags = backup_flags; memcpy(request->cdb, backup_cdb, SCSI_MAX_CDB_SIZE); request->cdb_length = backup_cdb_len; request->sort = backup_sort; request->timeout = backup_timeout; request->data = backup_data; request->sg_list = backup_sg_list; request->sg_count = backup_sg_count; request->data_length = backup_data_len; if (res.action == err_act_ok) res.action = err_act_retry; } } while ((res.action == err_act_retry && retries++ < 3) || (res.action == err_act_many_retries && retries++ < 30)); return res.error_code; }
static err_res send_tur(scsi_periph_device_info *device, scsi_ccb *request) { scsi_cmd_tur *cmd = (scsi_cmd_tur *)request->cdb; request->flags = SCSI_DIR_NONE | SCSI_ORDERED_QTAG; request->data = NULL; request->sg_list = NULL; request->data_length = 0; request->timeout = device->std_timeout; request->sort = -1; request->sg_list = NULL; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = SCSI_OP_TEST_UNIT_READY; request->cdb_length = sizeof(*cmd); device->scsi->sync_io(request); return periph_check_error(device, request); }
/*! Universal read/write function */ static status_t read_write(scsi_periph_device_info *device, scsi_ccb *request, io_operation *operation, uint64 offset, size_t originalNumBlocks, physical_entry* vecs, size_t vecCount, bool isWrite, size_t* _bytesTransferred) { uint32 blockSize = device->block_size; size_t numBlocks = originalNumBlocks; uint32 pos = offset; err_res res; int retries = 0; do { size_t numBytes; bool isReadWrite10 = false; request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN; // io_operations are generated by a DMAResource and thus contain DMA // safe physical vectors if (operation != NULL) request->flags |= SCSI_DMA_SAFE; // make sure we avoid 10 byte commands if they aren't supported if (!device->rw10_enabled || device->preferred_ccb_size == 6) { // restricting transfer is OK - the block manager will // take care of transferring the rest if (numBlocks > 0x100) numBlocks = 0x100; // no way to break the 21 bit address limit if (offset > 0x200000) return B_BAD_VALUE; // don't allow transfer cross the 24 bit address limit // (I'm not sure whether this is allowed, but this way we // are sure to not ask for trouble) if (offset < 0x100000) numBlocks = min_c(numBlocks, 0x100000 - pos); } numBytes = numBlocks * blockSize; if (numBlocks != originalNumBlocks) panic("I/O operation would need to be cut."); request->data = NULL; request->sg_list = vecs; request->data_length = numBytes; request->sg_count = vecCount; request->io_operation = operation; request->sort = pos; request->timeout = device->std_timeout; // see whether daemon instructed us to post an ordered command; // reset flag after read SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s", (int)request->flags, (int)device->next_tag_action, (request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no"); // use shortest commands whenever possible if (offset + numBlocks < 0x200000LL && numBlocks <= 0x100) { scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb; isReadWrite10 = false; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = isWrite ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6; cmd->high_lba = (pos >> 16) & 0x1f; cmd->mid_lba = (pos >> 8) & 0xff; cmd->low_lba = pos & 0xff; cmd->length = numBlocks; request->cdb_length = sizeof(*cmd); } else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000) { scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb; isReadWrite10 = true; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = isWrite ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10; cmd->relative_address = 0; cmd->force_unit_access = 0; cmd->disable_page_out = 0; cmd->lba = B_HOST_TO_BENDIAN_INT32(pos); cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks); request->cdb_length = sizeof(*cmd); } else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000000) { scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = isWrite ? SCSI_OP_WRITE_12 : SCSI_OP_READ_12; cmd->relative_address = 0; cmd->force_unit_access = 0; cmd->disable_page_out = 0; cmd->lba = B_HOST_TO_BENDIAN_INT32(pos); cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks); request->cdb_length = sizeof(*cmd); } else { scsi_cmd_rw_16 *cmd = (scsi_cmd_rw_16 *)request->cdb; memset(cmd, 0, sizeof(*cmd)); cmd->opcode = isWrite ? SCSI_OP_WRITE_16 : SCSI_OP_READ_16; cmd->force_unit_access_non_volatile = 0; cmd->force_unit_access = 0; cmd->disable_page_out = 0; cmd->lba = B_HOST_TO_BENDIAN_INT64(offset); cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks); request->cdb_length = sizeof(*cmd); } // TODO: last chance to detect errors that occured during concurrent accesses //status_t status = handle->pending_error; //if (status != B_OK) // return status; device->scsi->async_io(request); acquire_sem(request->completion_sem); // ask generic peripheral layer what to do now res = periph_check_error(device, request); // TODO: bytes might have been transferred even in the error case! switch (res.action) { case err_act_ok: *_bytesTransferred = numBytes - request->data_resid; break; case err_act_start: res = periph_send_start_stop(device, request, 1, device->removable); if (res.action == err_act_ok) res.action = err_act_retry; break; case err_act_invalid_req: // if this was a 10 byte command, the device probably doesn't // support them, so disable them and retry if (isReadWrite10) { atomic_and(&device->rw10_enabled, 0); res.action = err_act_retry; } else res.action = err_act_fail; break; } } while ((res.action == err_act_retry && retries++ < 3)