git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16938 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2006-03-30 20:43:13 +00:00
parent 2ed4099289
commit af29aba8a6
+145 -144
View File
@@ -1,7 +1,9 @@
/* /*
** Copyright 2002/03, Thomas Kurschel. All rights reserved. * Copyright 2004-2006, Haiku, Inc. All RightsReserved.
** Distributed under the terms of the OpenBeOS License. * Copyright 2002/03, Thomas Kurschel. All rights reserved.
*/ *
* Distributed under the terms of the MIT License.
*/
/* /*
Part of Open IDE bus manager Part of Open IDE bus manager
@@ -24,23 +26,25 @@
#define IDE_ATAPI_BUFFER_SIZE 512 #define IDE_ATAPI_BUFFER_SIZE 512
// set sense according to error reported by device /** Set sense according to error reported by device
// return: true - device reported error * return: true - device reported error
static bool check_packet_error( ide_device_info *device, ide_qrequest *qrequest ) */
static bool
check_packet_error(ide_device_info *device, ide_qrequest *qrequest)
{ {
ide_bus_info *bus = device->bus; ide_bus_info *bus = device->bus;
int status; int status;
status = bus->controller->get_altstatus( bus->channel ); status = bus->controller->get_altstatus(bus->channel);
if( (status & (ide_status_err | ide_status_df)) != 0 ) { if ((status & (ide_status_err | ide_status_df)) != 0) {
int error; int error;
SHOW_FLOW( 3, "packet error, status=%02x", status ); SHOW_FLOW(3, "packet error, status=%02x", status);
if( bus->controller->read_command_block_regs( bus->channel, if (bus->controller->read_command_block_regs(bus->channel,
&device->tf, ide_mask_error ) != B_OK ) &device->tf, ide_mask_error) != B_OK) {
{
device->subsys_status = SCSI_HBA_ERR; device->subsys_status = SCSI_HBA_ERR;
return true; return true;
} }
@@ -49,13 +53,13 @@ static bool check_packet_error( ide_device_info *device, ide_qrequest *qrequest
// we don't want to clutter syslog with "not ready" and UA messages, // we don't want to clutter syslog with "not ready" and UA messages,
// so use FLOW messages for them // so use FLOW messages for them
error = device->tf.read.error; error = device->tf.read.error;
if( (error >> 4) == SCSIS_KEY_NOT_READY || if ((error >> 4) == SCSIS_KEY_NOT_READY
(error >> 4) == SCSIS_KEY_UNIT_ATTENTION ) || (error >> 4) == SCSIS_KEY_UNIT_ATTENTION)
SHOW_FLOW( 3, "error=%x", error ); SHOW_FLOW(3, "error=%x", error);
else else
SHOW_ERROR( 3, "error=%x", error ); SHOW_ERROR(3, "error=%x", error);
// ATAPI says that // ATAPI says that:
// "ABRT shall be set to one if the requested command has been command // "ABRT shall be set to one if the requested command has been command
// aborted because the command code or a command parameter is invalid. // aborted because the command code or a command parameter is invalid.
// ABRT may be set to one if the device is not able to complete the // ABRT may be set to one if the device is not able to complete the
@@ -64,9 +68,9 @@ static bool check_packet_error( ide_device_info *device, ide_qrequest *qrequest
// if the medium got changed. Therefore, we currently ignore the bit // if the medium got changed. Therefore, we currently ignore the bit
// and rely on auto-sense information // and rely on auto-sense information
/* /*
if( (error & ide_error_abrt) != 0 ) { if ((error & ide_error_abrt) != 0) {
// if command got aborted, there's no point in reading sense // if command got aborted, there's no point in reading sense
set_sense( device, SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE ); set_sense(device, SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE);
return false; return false;
} }
*/ */
@@ -84,8 +88,10 @@ static bool check_packet_error( ide_device_info *device, ide_qrequest *qrequest
} }
// IRQ handler of packet transfer (executed as DPC) /** IRQ handler of packet transfer (executed as DPC) */
void packet_dpc( ide_qrequest *qrequest )
void
packet_dpc(ide_qrequest *qrequest)
{ {
ide_device_info *device = qrequest->device; ide_device_info *device = qrequest->device;
ide_bus_info *bus = device->bus; ide_bus_info *bus = device->bus;
@@ -93,32 +99,30 @@ void packet_dpc( ide_qrequest *qrequest )
uint32 timeout = qrequest->request->timeout > 0 ? uint32 timeout = qrequest->request->timeout > 0 ?
qrequest->request->timeout : IDE_STD_TIMEOUT; qrequest->request->timeout : IDE_STD_TIMEOUT;
SHOW_FLOW0( 3, "" ); SHOW_FLOW0(3, "");
bus->controller->read_command_block_regs( bus->channel, bus->controller->read_command_block_regs(bus->channel,
&device->tf, ide_mask_error | ide_mask_ireason ); &device->tf, ide_mask_error | ide_mask_ireason);
status = bus->controller->get_altstatus( bus->channel ); status = bus->controller->get_altstatus(bus->channel);
if( qrequest->packet_irq ) { if (qrequest->packet_irq) {
// device requests packet // device requests packet
qrequest->packet_irq = false; qrequest->packet_irq = false;
if( !device->tf.packet_res.cmd_or_data || if (!device->tf.packet_res.cmd_or_data
device->tf.packet_res.input_or_output || || device->tf.packet_res.input_or_output
(status & ide_status_drq) == 0 ) || (status & ide_status_drq) == 0) {
{
device->subsys_status = SCSI_SEQUENCE_FAIL; device->subsys_status = SCSI_SEQUENCE_FAIL;
goto err; goto err;
} }
start_waiting_nolock( device->bus, timeout, ide_state_async_waiting ); start_waiting_nolock(device->bus, timeout, ide_state_async_waiting);
// send packet // send packet
if( bus->controller->write_pio( bus->channel, if (bus->controller->write_pio(bus->channel,
(uint16 *)device->packet, sizeof( device->packet ) / sizeof( uint16 ), (uint16 *)device->packet, sizeof(device->packet) / sizeof(uint16),
true ) != B_OK ) true) != B_OK) {
{
SHOW_ERROR0( 1, "Error sending command packet" ); SHOW_ERROR0( 1, "Error sending command packet" );
device->subsys_status = SCSI_HBA_ERR; device->subsys_status = SCSI_HBA_ERR;
@@ -128,150 +132,147 @@ void packet_dpc( ide_qrequest *qrequest )
return; return;
} }
if( qrequest->uses_dma ) { if (qrequest->uses_dma) {
// DMA transmission finished // DMA transmission finished
bool dma_err, dev_err; bool dma_err, dev_err;
// don't check drq - if there is some data left, we cannot handle // don't check drq - if there is some data left, we cannot handle
// it anyway // it anyway
// XXX does the device throw remaining data away on DMA overflow? // XXX does the device throw remaining data away on DMA overflow?
SHOW_FLOW0( 3, "DMA done" ); SHOW_FLOW0(3, "DMA done");
dma_err = !finish_dma( device ); dma_err = !finish_dma(device);
dev_err = check_packet_error( device, qrequest ); dev_err = check_packet_error(device, qrequest);
// what to do if both the DMA controller and the device reports an error? // what to do if both the DMA controller and the device reports an error?
// let's assume that the DMA controller got problems because there was a // let's assume that the DMA controller got problems because there was a
// device error, so we ignore the dma error and use the device error instead // device error, so we ignore the dma error and use the device error instead
if( dev_err ) { if (dev_err) {
finish_checksense( qrequest ); finish_checksense(qrequest);
return; return;
} }
// device is happy, let's see what the controller says // device is happy, let's see what the controller says
if( !dma_err ) { if (!dma_err) {
// if DMA works, reset error counter so we don't disable // if DMA works, reset error counter so we don't disable
// DMA only because it didn't work once in a while // DMA only because it didn't work once in a while
device->DMA_failures = 0; device->DMA_failures = 0;
// this is a lie, but there is no way to find out // this is a lie, but there is no way to find out
// how much has been transmitted // how much has been transmitted
qrequest->request->data_resid = 0; qrequest->request->data_resid = 0;
finish_checksense( qrequest ); finish_checksense(qrequest);
} else { } else {
// DMA transmission went wrong // DMA transmission went wrong
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE ); set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE);
if( ++device->DMA_failures >= MAX_DMA_FAILURES ) { if (++device->DMA_failures >= MAX_DMA_FAILURES) {
SHOW_ERROR0( 1, "Disabling DMA because of too many errors" ); SHOW_ERROR0(1, "Disabling DMA because of too many errors");
device->DMA_enabled = false; device->DMA_enabled = false;
} }
finish_checksense( qrequest ); finish_checksense(qrequest);
} }
return; return;
} }
// PIO mode // PIO mode
if( (status & ide_status_drq) != 0 ) { if ((status & ide_status_drq) != 0) {
// device wants to transmit data // device wants to transmit data
int length; int length;
status_t err; status_t err;
SHOW_FLOW0( 3, "data transmission" ); SHOW_FLOW0(3, "data transmission");
if( device->tf.packet_res.cmd_or_data ) { if (device->tf.packet_res.cmd_or_data) {
device->subsys_status = SCSI_SEQUENCE_FAIL; device->subsys_status = SCSI_SEQUENCE_FAIL;
goto err; goto err;
} }
// check whether transmission direction matches // check whether transmission direction matches
if( (device->tf.packet_res.input_or_output ^ qrequest->is_write) == 0 ) { if ((device->tf.packet_res.input_or_output ^ qrequest->is_write) == 0) {
SHOW_ERROR0( 2, "data transmission in wrong way!?" ); SHOW_ERROR0(2, "data transmission in wrong way!?");
// XXX hm, either the device is broken or the caller has specified // TODO: hm, either the device is broken or the caller has specified
// the wrong direction - what is the proper handling? // the wrong direction - what is the proper handling?
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE ); set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE);
// reset device to make it alive // reset device to make it alive
// TBD: the device will abort next command with a reset condition // TODO: the device will abort next command with a reset condition
// perhaps we should hide that by reading sense? // perhaps we should hide that by reading sense?
SHOW_FLOW0( 3, "Reset" ); SHOW_FLOW0(3, "Reset");
reset_device( device, qrequest ); reset_device(device, qrequest);
finish_checksense( qrequest );
finish_checksense(qrequest);
return; return;
} }
// ask device how much data it wants to transmit // ask device how much data it wants to transmit
bus->controller->read_command_block_regs( bus->channel, bus->controller->read_command_block_regs(bus->channel,
&device->tf, ide_mask_byte_count ); &device->tf, ide_mask_byte_count);
length = device->tf.packet_res.byte_count_0_7 | length = device->tf.packet_res.byte_count_0_7
((int)device->tf.packet_res.byte_count_8_15 << 8); | ((int)device->tf.packet_res.byte_count_8_15 << 8);
SHOW_FLOW( 3, "device transmittes %d bytes", length ); SHOW_FLOW(3, "device transmittes %d bytes", length);
// start waiting before starting transmission, else we // start waiting before starting transmission, else we
// could start waiting too late; // could start waiting too late;
// don't mind getting overtaken by IRQ handler - as it will // don't mind getting overtaken by IRQ handler - as it will
// issue a DPC for the thread context we are in, we are save // issue a DPC for the thread context we are in, we are save
start_waiting_nolock( device->bus, timeout, ide_state_async_waiting ); start_waiting_nolock(device->bus, timeout, ide_state_async_waiting);
if( device->tf.packet_res.input_or_output ) if (device->tf.packet_res.input_or_output)
err = read_PIO_block( qrequest, length ); err = read_PIO_block(qrequest, length);
else else
err = write_PIO_block( qrequest, length ); err = write_PIO_block(qrequest, length);
// only report "real" errors; // only report "real" errors;
// discarding data (ERR_TOO_BIG) can happen but is OK // discarding data (ERR_TOO_BIG) can happen but is OK
if( err == B_ERROR ) { if (err == B_ERROR) {
SHOW_ERROR0( 2, "Error during PIO transmission" ); SHOW_ERROR0(2, "Error during PIO transmission");
device->subsys_status = SCSI_HBA_ERR; device->subsys_status = SCSI_HBA_ERR;
goto err_cancel_timer; goto err_cancel_timer;
} }
SHOW_FLOW0( 3, "7" ); SHOW_FLOW0(3, "7");
return; return;
} else { } else {
// device has done job and doesn't want to transmit data anymore // device has done job and doesn't want to transmit data anymore
// -> finish request // -> finish request
SHOW_FLOW0( 3, "no data" ); SHOW_FLOW0(3, "no data");
check_packet_error( device, qrequest ); check_packet_error(device, qrequest);
SHOW_FLOW( 3, "finished: %d of %d left", SHOW_FLOW(3, "finished: %d of %d left",
(int)qrequest->request->data_resid, (int)qrequest->request->data_resid,
(int)qrequest->request->data_len ); (int)qrequest->request->data_len);
finish_checksense( qrequest ); finish_checksense(qrequest);
return; return;
} }
return; return;
err_cancel_timer: err_cancel_timer:
cancel_irq_timeout( device->bus ); cancel_irq_timeout(device->bus);
err: err:
finish_checksense( qrequest ); finish_checksense(qrequest);
} }
// create taskfile for ATAPI packet /** Create taskfile for ATAPI packet */
static bool create_packet_taskfile( ide_device_info *device, ide_qrequest *qrequest,
bool write ) static bool
create_packet_taskfile(ide_device_info *device, ide_qrequest *qrequest,
bool write)
{ {
scsi_ccb *request = qrequest->request; scsi_ccb *request = qrequest->request;
SHOW_FLOW( 3, "DMA enabled=%d, uses_dma=%d, scsi_cmd=%x", SHOW_FLOW(3, "DMA enabled=%d, uses_dma=%d, scsi_cmd=%x",
device->DMA_enabled, qrequest->uses_dma, device->packet[0] ); device->DMA_enabled, qrequest->uses_dma, device->packet[0]);
device->tf_param_mask = ide_mask_features | ide_mask_byte_count; device->tf_param_mask = ide_mask_features | ide_mask_byte_count;
@@ -285,8 +286,10 @@ static bool create_packet_taskfile( ide_device_info *device, ide_qrequest *qrequ
} }
// send ATAPI packet /** Send ATAPI packet */
void send_packet( ide_device_info *device, ide_qrequest *qrequest, bool write )
void
send_packet(ide_device_info *device, ide_qrequest *qrequest, bool write)
{ {
ide_bus_info *bus = device->bus; ide_bus_info *bus = device->bus;
bool packet_irq = device->atapi.packet_irq; bool packet_irq = device->atapi.packet_irq;
@@ -326,61 +329,61 @@ void send_packet( ide_device_info *device, ide_qrequest *qrequest, bool write )
scsi_cmd == SCSI_OP_READ_CD ); scsi_cmd == SCSI_OP_READ_CD );
// try preparing DMA, if that fails, fall back to PIO // try preparing DMA, if that fails, fall back to PIO
if( qrequest->uses_dma ) { if (qrequest->uses_dma) {
SHOW_FLOW0( 3, "0" ); SHOW_FLOW0(3, "0");
if( !prepare_dma( device, qrequest )) if (!prepare_dma( device, qrequest))
qrequest->uses_dma = false; qrequest->uses_dma = false;
SHOW_FLOW( 3, "0->%d", qrequest->uses_dma ); SHOW_FLOW(3, "0->%d", qrequest->uses_dma);
} }
SHOW_FLOW0( 3, "1" ); SHOW_FLOW0(3, "1");
if( !qrequest->uses_dma ) if (!qrequest->uses_dma)
prep_PIO_transfer( device, qrequest ); prep_PIO_transfer(device, qrequest);
SHOW_FLOW0( 3, "2" ); SHOW_FLOW0(3, "2");
if( !create_packet_taskfile( device, qrequest, write )) if (!create_packet_taskfile(device, qrequest, write))
goto err_setup; goto err_setup;
SHOW_FLOW0( 3, "3" ); SHOW_FLOW0(3, "3");
if( !send_command( device, qrequest, false, if (!send_command(device, qrequest, false,
device->atapi.packet_irq_timeout, device->atapi.packet_irq_timeout,
device->atapi.packet_irq ? ide_state_async_waiting : ide_state_accessing )) device->atapi.packet_irq ? ide_state_async_waiting : ide_state_accessing))
goto err_setup; goto err_setup;
SHOW_FLOW0( 3, "4" ); SHOW_FLOW0(3, "4");
if( packet_irq ) if (packet_irq) {
// device asks for packet via IRQ; // device asks for packet via IRQ;
// timeout and stuff is already set by send_command // timeout and stuff is already set by send_command
return; return;
}
SHOW_FLOW0( 3, "5" ); SHOW_FLOW0(3, "5");
// wait for device to get ready for packet transmission // wait for device to get ready for packet transmission
if( !ide_wait( device, ide_status_drq, ide_status_bsy, false, 100000 )) if (!ide_wait(device, ide_status_drq, ide_status_bsy, false, 100000))
goto err_setup; goto err_setup;
SHOW_FLOW0( 3, "6" ); SHOW_FLOW0(3, "6");
// make sure device really asks for command packet // make sure device really asks for command packet
bus->controller->read_command_block_regs( bus->channel, &device->tf, bus->controller->read_command_block_regs(bus->channel, &device->tf,
ide_mask_ireason ); ide_mask_ireason);
if( !device->tf.packet_res.cmd_or_data || if (!device->tf.packet_res.cmd_or_data
device->tf.packet_res.input_or_output ) || device->tf.packet_res.input_or_output) {
{
device->subsys_status = SCSI_SEQUENCE_FAIL; device->subsys_status = SCSI_SEQUENCE_FAIL;
goto err_setup; goto err_setup;
} }
SHOW_FLOW0( 3, "7" ); SHOW_FLOW0(3, "7");
// some old drives need a delay before submitting the packet // some old drives need a delay before submitting the packet
spin( 10 ); spin(10);
// locking is evil here: as soon as the packet is transmitted, the device // locking is evil here: as soon as the packet is transmitted, the device
// may raise an IRQ (which actually happens if the device reports an Check // may raise an IRQ (which actually happens if the device reports an Check
@@ -390,77 +393,75 @@ void send_packet( ide_device_info *device, ide_qrequest *qrequest, bool write )
// sent (avoid sending 16 bits as controller may transmit 32 bit chunks) // sent (avoid sending 16 bits as controller may transmit 32 bit chunks)
// write packet // write packet
if( bus->controller->write_pio( bus->channel, if (bus->controller->write_pio(bus->channel,
(uint16 *)device->packet, sizeof( device->packet ) / sizeof( uint16 ) - 2, (uint16 *)device->packet, sizeof(device->packet) / sizeof(uint16) - 2,
true ) != B_OK ) true) != B_OK) {
{
goto err_packet; goto err_packet;
} }
IDE_LOCK( bus ); IDE_LOCK(bus);
if( bus->controller->write_pio( bus->channel, if (bus->controller->write_pio(bus->channel,
(uint16 *)device->packet + sizeof( device->packet ) / sizeof( uint16 ) - 2, (uint16 *)device->packet + sizeof(device->packet) / sizeof(uint16) - 2,
2, true ) != B_OK ) 2, true) != B_OK) {
{
goto err_packet2; goto err_packet2;
} }
if( qrequest->uses_dma ) { if (qrequest->uses_dma) {
SHOW_FLOW0( 3, "ready for DMA" ); SHOW_FLOW0( 3, "ready for DMA" );
// S/G table must already be setup - we hold the bus lock, so // S/G table must already be setup - we hold the bus lock, so
// we really have to hurry up // we really have to hurry up
start_dma_wait( device, qrequest ); start_dma_wait(device, qrequest);
} else { } else {
uint32 timeout = qrequest->request->timeout > 0 ? uint32 timeout = qrequest->request->timeout > 0 ?
qrequest->request->timeout : IDE_STD_TIMEOUT; qrequest->request->timeout : IDE_STD_TIMEOUT;
start_waiting( bus, timeout, ide_state_async_waiting ); start_waiting(bus, timeout, ide_state_async_waiting);
} }
SHOW_FLOW0( 3, "8" ); SHOW_FLOW0(3, "8");
return; return;
err_packet2: err_packet2:
IDE_UNLOCK( bus ); IDE_UNLOCK(bus);
err_packet: err_packet:
device->subsys_status = SCSI_HBA_ERR; device->subsys_status = SCSI_HBA_ERR;
err_setup: err_setup:
if( qrequest->uses_dma ) if (qrequest->uses_dma)
abort_dma( device, qrequest ); abort_dma(device, qrequest);
finish_checksense( qrequest ); finish_checksense(qrequest);
} }
// execute SCSI I/O for atapi devices /** Execute SCSI I/O for atapi devices */
void atapi_exec_io( ide_device_info *device, ide_qrequest *qrequest )
void
atapi_exec_io(ide_device_info *device, ide_qrequest *qrequest)
{ {
scsi_ccb *request = qrequest->request; scsi_ccb *request = qrequest->request;
SHOW_FLOW( 3, "command=%x", qrequest->request->cdb[0] ); SHOW_FLOW(3, "command=%x", qrequest->request->cdb[0]);
// ATAPI command packets are 12 bytes long; // ATAPI command packets are 12 bytes long;
// if the command is shorter, remaining bytes must be padded with zeros // if the command is shorter, remaining bytes must be padded with zeros
memset( device->packet, 0, sizeof( device->packet )); memset(device->packet, 0, sizeof(device->packet));
memcpy( device->packet, request->cdb, request->cdb_len ); memcpy(device->packet, request->cdb, request->cdb_len);
if( request->cdb[0] == SCSI_OP_REQUEST_SENSE && device->combined_sense ) { if (request->cdb[0] == SCSI_OP_REQUEST_SENSE && device->combined_sense) {
// we have a pending emulated sense - return it on REQUEST SENSE // we have a pending emulated sense - return it on REQUEST SENSE
ide_request_sense( device, qrequest ); ide_request_sense(device, qrequest);
finish_checksense( qrequest ); finish_checksense(qrequest);
} else { } else {
// reset all error codes for new request // reset all error codes for new request
start_request( device, qrequest ); start_request(device, qrequest);
// now we have an IDE packet // now we have an IDE packet
send_packet( device, qrequest, send_packet(device, qrequest,
(request->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT ); (request->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT);
} }
} }
@@ -472,7 +473,7 @@ prep_atapi(ide_device_info *device)
{ {
ide_device_infoblock *infoblock = &device->infoblock; ide_device_infoblock *infoblock = &device->infoblock;
SHOW_FLOW0( 3, "" ); SHOW_FLOW0(3, "");
device->is_atapi = true; device->is_atapi = true;
device->exec_io = atapi_exec_io; device->exec_io = atapi_exec_io;