From 455b379c2799703a11b8d17521840808f11afe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 1 Jun 2010 13:37:55 +0000 Subject: [PATCH] * Added a read_write() function to the scsi_periph module. * Internally, moved the contents of periph_io() into a static read_write() function, and use it from the new periph_read_write() as well. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36988 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/drivers/scsi_periph.h | 3 + src/add-ons/kernel/generic/scsi_periph/io.cpp | 285 ++++++++++-------- .../generic/scsi_periph/scsi_periph.cpp | 1 + .../generic/scsi_periph/scsi_periph_int.h | 3 + 4 files changed, 158 insertions(+), 134 deletions(-) diff --git a/headers/private/drivers/scsi_periph.h b/headers/private/drivers/scsi_periph.h index ccfaae9d45..03fd57e4f5 100644 --- a/headers/private/drivers/scsi_periph.h +++ b/headers/private/drivers/scsi_periph.h @@ -101,6 +101,9 @@ typedef struct scsi_periph_interface { status_t (*handle_free)(scsi_periph_handle handle); // *** default implementation for block devices *** + status_t (*read_write)(scsi_periph_device_info *device, scsi_ccb *request, + uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount, + bool isWrite, size_t* _bytesTransferred); status_t (*io)(scsi_periph_device device, io_operation *operation, size_t *_bytesTransferred); diff --git a/src/add-ons/kernel/generic/scsi_periph/io.cpp b/src/add-ons/kernel/generic/scsi_periph/io.cpp index 33afc4bbfb..caef4cb421 100644 --- a/src/add-ons/kernel/generic/scsi_periph/io.cpp +++ b/src/add-ons/kernel/generic/scsi_periph/io.cpp @@ -104,6 +104,140 @@ raw_command(scsi_periph_device_info *device, raw_device_command *cmd) } +/*! 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; + + request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN; + + // 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 (pos > 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 (pos < 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 (pos + numBlocks < 0x200000 && 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 { + 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); + } + + // 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) + || (res.action == err_act_many_retries && retries++ < 30)); + + // peripheral layer only created "read" error, so we have to + // map them to "write" errors if this was a write request + if (res.error_code == B_DEV_READ_ERROR && isWrite) + return B_DEV_WRITE_ERROR; + + return res.error_code; +} + + +// #pragma mark - public functions + + status_t periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer, size_t length) @@ -157,150 +291,33 @@ periph_sync_queue_daemon(void *arg, int iteration) } -/*! Universal read/write function */ +status_t +periph_read_write(scsi_periph_device_info *device, scsi_ccb *request, + uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount, + bool isWrite, size_t* _bytesTransferred) +{ + return read_write(device, request, NULL, offset, numBlocks, vecs, vecCount, + isWrite, _bytesTransferred); +} + + status_t periph_io(scsi_periph_device_info *device, io_operation *operation, size_t* _bytesTransferred) { - uint32 blockSize = device->block_size; - size_t numBlocks = operation->Length() / blockSize; - uint32 pos = operation->Offset() / blockSize; - scsi_ccb *request; - err_res res; - int retries = 0; - int err; + const uint32 blockSize = device->block_size; // don't test rw10_enabled restrictions - this flag may get changed - request = device->scsi->alloc_ccb(device->scsi_device); - + scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device); if (request == NULL) return B_NO_MEMORY; - do { - size_t numBytes; - bool is_rw10; - - request->flags = operation->IsWrite() ? SCSI_DIR_OUT : SCSI_DIR_IN; - - // 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 (pos > 0x200000) { - err = B_BAD_VALUE; - goto abort; - } - - // 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 (pos < 0x100000) - numBlocks = min_c(numBlocks, 0x100000 - pos); - } - - numBytes = numBlocks * blockSize; - if (numBytes != operation->Length()) - panic("I/O operation would need to be cut."); - - request->data = NULL; - request->sg_list = (physical_entry*)operation->Vecs(); - request->data_length = numBytes; - request->sg_count = operation->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 (pos + numBlocks < 0x200000 && numBlocks <= 0x100) { - scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb; - - is_rw10 = false; - - memset(cmd, 0, sizeof(*cmd)); - cmd->opcode = operation->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 { - scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb; - - is_rw10 = true; - - memset(cmd, 0, sizeof(*cmd)); - cmd->opcode = operation->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); - } - - // last chance to detect errors that occured during concurrent accesses - err = 0; // TODO: handle->pending_error; - - if (err) - goto abort; - - 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 (is_rw10) { - 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) - || (res.action == err_act_many_retries && retries++ < 30)); + status_t status = read_write(device, request, operation, + operation->Offset() / blockSize, operation->Length() / blockSize, + (physical_entry *)operation->Vecs(), operation->VecCount(), + operation->IsWrite(), _bytesTransferred); device->scsi->free_ccb(request); - - // peripheral layer only created "read" error, so we have to - // map them to "write" errors if this was a write request - if (res.error_code == B_DEV_READ_ERROR && operation->IsWrite()) - return B_DEV_WRITE_ERROR; - - return res.error_code; - -abort: - device->scsi->free_ccb(request); - return err; + return status; } diff --git a/src/add-ons/kernel/generic/scsi_periph/scsi_periph.cpp b/src/add-ons/kernel/generic/scsi_periph/scsi_periph.cpp index 5946c098a5..c3b166347c 100644 --- a/src/add-ons/kernel/generic/scsi_periph/scsi_periph.cpp +++ b/src/add-ons/kernel/generic/scsi_periph/scsi_periph.cpp @@ -131,6 +131,7 @@ static scsi_periph_interface sSCSIPeripheralModule = { periph_handle_close, periph_handle_free, + periph_read_write, periph_io, periph_ioctl, periph_check_capacity, diff --git a/src/add-ons/kernel/generic/scsi_periph/scsi_periph_int.h b/src/add-ons/kernel/generic/scsi_periph/scsi_periph_int.h index 8c83aee933..c7493a5b31 100644 --- a/src/add-ons/kernel/generic/scsi_periph/scsi_periph_int.h +++ b/src/add-ons/kernel/generic/scsi_periph/scsi_periph_int.h @@ -91,6 +91,9 @@ char *periph_compose_device_name(device_node *device_node, const char *prefix); // io.c +status_t periph_read_write(scsi_periph_device_info *device, scsi_ccb *request, + uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount, + bool isWrite, size_t* _bytesTransferred); status_t periph_io(scsi_periph_device_info* device, io_operation* operation, size_t *_bytesTransferred); status_t periph_ioctl(scsi_periph_handle_info *handle, int op,