Added IDE bus manager written by Thomas Kurschel.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7775 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
b7c4739649
commit
debb0e18cc
23
src/add-ons/kernel/bus_managers/ide/Jamfile
Normal file
23
src/add-ons/kernel/bus_managers/ide/Jamfile
Normal file
@ -0,0 +1,23 @@
|
||||
SubDir OBOS_TOP src add-ons kernel bus_managers ide ;
|
||||
|
||||
UsePrivateHeaders kernel ;
|
||||
UsePrivateHeaders [ FDirName kernel arch $(OBOS_ARCH) ] ;
|
||||
UsePrivateHeaders [ FDirName kernel boot platform $(OBOS_BOOT_PLATFORM) ] ;
|
||||
|
||||
KernelAddon ide : kernel bus_managers :
|
||||
ata.c
|
||||
atapi.c
|
||||
basic_protocol.c
|
||||
channels.c
|
||||
devices.c
|
||||
dma.c
|
||||
emulation.c
|
||||
ide.c
|
||||
ide_sim.c
|
||||
pio.c
|
||||
queuing.c
|
||||
scsi2ata.c
|
||||
sync.c
|
||||
virtual_memory.c
|
||||
;
|
||||
|
45
src/add-ons/kernel/bus_managers/ide/KernelExport_ext.h
Normal file
45
src/add-ons/kernel/bus_managers/ide/KernelExport_ext.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Functions that are missing in kernel.
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_EXPORT_EXT_H
|
||||
#define _KERNEL_EXPORT_EXT_H
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <iovec.h>
|
||||
|
||||
|
||||
// get memory map of iovec
|
||||
status_t get_iovec_memory_map(
|
||||
iovec *vec, // iovec to analyze
|
||||
size_t vec_count, // number of entries in vec
|
||||
size_t vec_offset, // number of bytes to skip at beginning of vec
|
||||
size_t len, // number of bytes to analyze
|
||||
physical_entry *map, // resulting memory map
|
||||
size_t max_entries, // max number of entries in map
|
||||
size_t *num_entries, // actual number of map entries used
|
||||
size_t *mapped_len // actual number of bytes described by map
|
||||
);
|
||||
|
||||
// map main memory into virtual address space
|
||||
status_t map_mainmemory(
|
||||
addr_t physical_addr, // physical address to map
|
||||
void **virtual_addr // receives corresponding virtual address
|
||||
);
|
||||
|
||||
// unmap main memory from virtual address space
|
||||
status_t unmap_mainmemory(
|
||||
void *virtual_addr // virtual address to release
|
||||
);
|
||||
|
||||
|
||||
// this should be moved to SupportDefs.h
|
||||
int32 atomic_exchange(vint32 *value, int32 newValue);
|
||||
|
||||
|
||||
#endif
|
781
src/add-ons/kernel/bus_managers/ide/ata.c
Normal file
781
src/add-ons/kernel/bus_managers/ide/ata.c
Normal file
@ -0,0 +1,781 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
ATA command protocol
|
||||
*/
|
||||
|
||||
|
||||
#include "ide_internal.h"
|
||||
|
||||
#include "ide_sim.h"
|
||||
#include "ide_cmds.h"
|
||||
|
||||
|
||||
/** verify that device is ready for further PIO transmission */
|
||||
|
||||
static bool
|
||||
check_rw_status(ide_device_info *device, bool drq_required)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
int status;
|
||||
|
||||
status = bus->controller->get_altstatus(bus->channel);
|
||||
|
||||
if ((status & ide_status_bsy) != 0) {
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drq_required != ((status & ide_status_drq) != 0)) {
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** DPC called at
|
||||
* - begin of each PIO read/write block
|
||||
* - end of PUI write transmission
|
||||
*/
|
||||
|
||||
void
|
||||
ata_dpc_PIO(ide_qrequest *qrequest)
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
uint32 timeout = qrequest->request->timeout > 0 ?
|
||||
qrequest->request->timeout : IDE_STD_TIMEOUT;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
if (check_rw_error(device, qrequest)
|
||||
|| !check_rw_status(device, qrequest->is_write ? device->left_blocks > 1 : true))
|
||||
{
|
||||
// failure reported by device
|
||||
SHOW_FLOW0( 3, "command finished unsuccessfully" );
|
||||
|
||||
finish_checksense(qrequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (qrequest->is_write) {
|
||||
if (device->left_blocks == 0) {
|
||||
// this was the end-of-transmission IRQ
|
||||
SHOW_FLOW0( 3, "write access finished" );
|
||||
if (!wait_for_drq(device)) {
|
||||
SHOW_ERROR0( 3, "device wants to transmit data though command is finished" );
|
||||
goto finish;
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// wait until device requests data
|
||||
SHOW_FLOW0( 3, "Waiting for device ready to transmit" );
|
||||
if (!wait_for_drq(device)) {
|
||||
SHOW_FLOW0( 3, "device not ready for data transmission - abort" );
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// start async waiting for next block/end of command
|
||||
// we should start that when block is transmitted, but with bad
|
||||
// luck the IRQ fires exactly between transmission and start of waiting,
|
||||
// so we better start waiting too early; as we are in service thread,
|
||||
// a DPC initiated by IRQ cannot overtake us, so there is no need to block
|
||||
// IRQs during sent
|
||||
if (device->left_blocks > 1)
|
||||
start_waiting_nolock(device->bus, timeout, ide_state_async_waiting);
|
||||
|
||||
// having a too short data buffer shouldn't happen here
|
||||
// anyway - we are prepared
|
||||
SHOW_FLOW0( 3, "Writing one block" );
|
||||
if (write_PIO_block(qrequest, 512) == B_ERROR)
|
||||
goto finish_cancel_timeout;
|
||||
|
||||
--device->left_blocks;
|
||||
} else {
|
||||
if (device->left_blocks > 1) {
|
||||
// start async waiting for next command (see above)
|
||||
start_waiting_nolock(device->bus, timeout, ide_state_async_waiting);
|
||||
}
|
||||
|
||||
// see write
|
||||
SHOW_FLOW0( 3, "Reading one block" );
|
||||
if (read_PIO_block(qrequest, 512) == B_ERROR)
|
||||
goto finish_cancel_timeout;
|
||||
|
||||
--device->left_blocks;
|
||||
|
||||
if (device->left_blocks == 0) {
|
||||
// at end of transmission, wait for data request going low
|
||||
SHOW_FLOW0( 3, "Waiting for device to finish transmission" );
|
||||
|
||||
if (!wait_for_drqdown(device))
|
||||
SHOW_FLOW0( 3, "Device continues data transmission - abort command" );
|
||||
|
||||
// we don't cancel timeout as no timeout is started during last block
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
finish_cancel_timeout:
|
||||
cancel_irq_timeout(device->bus);
|
||||
|
||||
finish:
|
||||
finish_checksense(qrequest);
|
||||
}
|
||||
|
||||
|
||||
/** DPC called when IRQ was fired at end of DMA transmission */
|
||||
|
||||
void
|
||||
ata_dpc_DMA(ide_qrequest *qrequest)
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
bool dma_success, dev_err;
|
||||
|
||||
dma_success = finish_dma(device);
|
||||
dev_err = check_rw_error(device, qrequest);
|
||||
|
||||
if (dma_success && !dev_err) {
|
||||
// reset error count if DMA worked
|
||||
device->DMA_failures = 0;
|
||||
device->CQ_failures = 0;
|
||||
qrequest->request->data_resid = 0;
|
||||
finish_checksense(qrequest);
|
||||
} else {
|
||||
SHOW_ERROR0( 2, "Error in DMA transmission" );
|
||||
|
||||
set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE);
|
||||
|
||||
if (++device->DMA_failures >= MAX_DMA_FAILURES) {
|
||||
SHOW_ERROR0( 2, "Disabled DMA because of too many errors" );
|
||||
device->DMA_enabled = false;
|
||||
}
|
||||
|
||||
// reset queue in case queuing is active
|
||||
finish_reset_queue(qrequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// list of LBA48 opcodes
|
||||
static uint8 cmd_48[2][2] = {
|
||||
{ IDE_CMD_READ_SECTORS_EXT, IDE_CMD_WRITE_SECTORS_EXT },
|
||||
{ IDE_CMD_READ_DMA_EXT, IDE_CMD_WRITE_DMA_EXT }
|
||||
};
|
||||
|
||||
|
||||
// list of normal LBA opcodes
|
||||
static uint8 cmd_28[2][2] = {
|
||||
{ IDE_CMD_READ_SECTORS, IDE_CMD_WRITE_SECTORS },
|
||||
{ IDE_CMD_READ_DMA, IDE_CMD_WRITE_DMA }
|
||||
};
|
||||
|
||||
|
||||
// create IDE read/write command
|
||||
static bool
|
||||
create_rw_taskfile(ide_device_info *device, ide_qrequest *qrequest,
|
||||
uint64 pos, size_t length, bool write)
|
||||
{
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
// XXX disable any writes
|
||||
/* if( write )
|
||||
goto err;*/
|
||||
|
||||
if (device->use_LBA) {
|
||||
if (device->use_48bits && (pos + length > 0xfffffff || length > 0x100)) {
|
||||
// use LBA48 only if necessary
|
||||
SHOW_FLOW0( 3, "using LBA48" );
|
||||
|
||||
if (length > 0xffff)
|
||||
goto err;
|
||||
|
||||
if (qrequest->queuable) {
|
||||
// queued LBA48
|
||||
device->tf_param_mask = ide_mask_features_48
|
||||
| ide_mask_sector_count
|
||||
| ide_mask_LBA_low_48
|
||||
| ide_mask_LBA_mid_48
|
||||
| ide_mask_LBA_high_48;
|
||||
|
||||
device->tf.queued48.sector_count_0_7 = length & 0xff;
|
||||
device->tf.queued48.sector_count_8_15 = (length >> 8) & 0xff;
|
||||
device->tf.queued48.tag = qrequest->tag;
|
||||
device->tf.queued48.lba_0_7 = pos & 0xff;
|
||||
device->tf.queued48.lba_8_15 = (pos >> 8) & 0xff;
|
||||
device->tf.queued48.lba_16_23 = (pos >> 16) & 0xff;
|
||||
device->tf.queued48.lba_24_31 = (pos >> 24) & 0xff;
|
||||
device->tf.queued48.lba_32_39 = (pos >> 32) & 0xff;
|
||||
device->tf.queued48.lba_40_47 = (pos >> 40) & 0xff;
|
||||
device->tf.queued48.command = write ? IDE_CMD_WRITE_DMA_QUEUED
|
||||
: IDE_CMD_READ_DMA_QUEUED;
|
||||
return true;
|
||||
} else {
|
||||
// non-queued LBA48
|
||||
device->tf_param_mask = ide_mask_sector_count_48
|
||||
| ide_mask_LBA_low_48
|
||||
| ide_mask_LBA_mid_48
|
||||
| ide_mask_LBA_high_48;
|
||||
|
||||
device->tf.lba48.sector_count_0_7 = length & 0xff;
|
||||
device->tf.lba48.sector_count_8_15 = (length >> 8) & 0xff;
|
||||
device->tf.lba48.lba_0_7 = pos & 0xff;
|
||||
device->tf.lba48.lba_8_15 = (pos >> 8) & 0xff;
|
||||
device->tf.lba48.lba_16_23 = (pos >> 16) & 0xff;
|
||||
device->tf.lba48.lba_24_31 = (pos >> 24) & 0xff;
|
||||
device->tf.lba48.lba_32_39 = (pos >> 32) & 0xff;
|
||||
device->tf.lba48.lba_40_47 = (pos >> 40) & 0xff;
|
||||
device->tf.lba48.command = cmd_48[qrequest->uses_dma][write];
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// normal LBA
|
||||
SHOW_FLOW0( 3, "using LBA" );
|
||||
|
||||
if (length > 0x100)
|
||||
goto err;
|
||||
|
||||
if (qrequest->queuable) {
|
||||
// queued LBA
|
||||
SHOW_FLOW( 3, "creating DMA queued command, tag=%d", qrequest->tag );
|
||||
device->tf_param_mask =
|
||||
ide_mask_features |
|
||||
ide_mask_sector_count |
|
||||
ide_mask_LBA_low |
|
||||
ide_mask_LBA_mid |
|
||||
ide_mask_LBA_high |
|
||||
ide_mask_device_head;
|
||||
|
||||
device->tf.queued.sector_count = length & 0xff;
|
||||
device->tf.queued.tag = qrequest->tag;
|
||||
device->tf.queued.lba_0_7 = pos & 0xff;
|
||||
device->tf.queued.lba_8_15 = (pos >> 8) & 0xff;
|
||||
device->tf.queued.lba_16_23 = (pos >> 16) & 0xff;
|
||||
device->tf.queued.lba_24_27 = (pos >> 24) & 0xf;
|
||||
device->tf.queued.command = write ? IDE_CMD_WRITE_DMA_QUEUED
|
||||
: IDE_CMD_READ_DMA_QUEUED;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// non-queued LBA
|
||||
SHOW_FLOW0( 3, "creating normal DMA/PIO command" );
|
||||
device->tf_param_mask =
|
||||
ide_mask_sector_count |
|
||||
ide_mask_LBA_low |
|
||||
ide_mask_LBA_mid |
|
||||
ide_mask_LBA_high |
|
||||
ide_mask_device_head;
|
||||
|
||||
device->tf.lba.sector_count = length & 0xff;
|
||||
device->tf.lba.lba_0_7 = pos & 0xff;
|
||||
device->tf.lba.lba_8_15 = (pos >> 8) & 0xff;
|
||||
device->tf.lba.lba_16_23 = (pos >> 16) & 0xff;
|
||||
device->tf.lba.lba_24_27 = (pos >> 24) & 0xf;
|
||||
device->tf.lba.command = cmd_28[qrequest->uses_dma][write];
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// CHS mode
|
||||
// (probably, noone would notice if we'd dropped support)
|
||||
uint32 track_size, cylinder_offset, cylinder;
|
||||
ide_device_infoblock *infoblock = &device->infoblock;
|
||||
|
||||
if( length > 0x100 )
|
||||
goto err;
|
||||
|
||||
device->tf.chs.mode = ide_mode_chs;
|
||||
|
||||
device->tf_param_mask =
|
||||
ide_mask_sector_count |
|
||||
ide_mask_sector_number |
|
||||
ide_mask_cylinder_low |
|
||||
ide_mask_cylinder_high |
|
||||
ide_mask_device_head;
|
||||
|
||||
device->tf.chs.sector_count = length & 0xff;
|
||||
|
||||
track_size = infoblock->current_heads * infoblock->current_sectors;
|
||||
|
||||
if( track_size == 0 ) {
|
||||
set_sense( device,
|
||||
SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_MEDIUM_FORMAT_CORRUPTED );
|
||||
return false;
|
||||
}
|
||||
|
||||
cylinder = pos / track_size;
|
||||
|
||||
device->tf.chs.cylinder_0_7 = cylinder & 0xff;
|
||||
device->tf.chs.cylinder_8_15 = (cylinder >> 8) & 0xff;
|
||||
|
||||
cylinder_offset = pos - cylinder * track_size;
|
||||
|
||||
device->tf.chs.sector_number = (cylinder_offset % infoblock->current_sectors + 1) & 0xff;
|
||||
device->tf.chs.head = cylinder_offset / infoblock->current_sectors;
|
||||
|
||||
device->tf.chs.command = cmd_28[qrequest->uses_dma][write];
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// execute read/write command
|
||||
// pos - first block
|
||||
// length - number of blocks
|
||||
void ata_send_rw( ide_device_info *device, ide_qrequest *qrequest,
|
||||
uint64 pos, size_t length, bool write )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint32 timeout;
|
||||
|
||||
// make a copy first as settings may get changed by user during execution
|
||||
qrequest->is_write = write;
|
||||
qrequest->uses_dma = device->DMA_enabled;
|
||||
|
||||
if( qrequest->uses_dma ) {
|
||||
if( !prepare_dma( device, qrequest )) {
|
||||
// fall back to PIO on error
|
||||
|
||||
// if command queueing is used and there is another command
|
||||
// already running, we cannot fallback to PIO immediately -> declare
|
||||
// command as not queuable and resubmit it, so the scsi bus manager
|
||||
// will block other requests on retry
|
||||
// (XXX this is not fine if the caller wants to recycle the CCB)
|
||||
if( device->num_running_reqs > 1 ) {
|
||||
qrequest->request->flags &= ~SCSI_ORDERED_QTAG;
|
||||
finish_retry( qrequest );
|
||||
return;
|
||||
}
|
||||
|
||||
qrequest->uses_dma = false;
|
||||
}
|
||||
}
|
||||
|
||||
if( !qrequest->uses_dma ) {
|
||||
prep_PIO_transfer( device, qrequest );
|
||||
device->left_blocks = length;
|
||||
}
|
||||
|
||||
// compose command
|
||||
if( !create_rw_taskfile( device, qrequest, pos, length, write ))
|
||||
goto err_setup;
|
||||
|
||||
// if no timeout is specified, use standard
|
||||
timeout = qrequest->request->timeout > 0 ?
|
||||
qrequest->request->timeout : IDE_STD_TIMEOUT;
|
||||
|
||||
// in DMA mode, we continue with "accessing",
|
||||
// on PIO read, we continue with "async waiting"
|
||||
// on PIO write, we continue with "accessing"
|
||||
if( !send_command( device, qrequest, !device->is_atapi, timeout,
|
||||
(!qrequest->uses_dma && !qrequest->is_write) ?
|
||||
ide_state_async_waiting : ide_state_accessing ))
|
||||
goto err_send;
|
||||
|
||||
if( qrequest->uses_dma ) {
|
||||
// if queuing used, we have to ask device first whether it wants
|
||||
// to postpone the command
|
||||
// XXX: using the bus release IRQ we don't have to busy wait for
|
||||
// a response, but I heard that IBM drives have problems with
|
||||
// that IRQ; to be evaluated
|
||||
if( qrequest->queuable ) {
|
||||
if( !wait_for_drdy( device ))
|
||||
goto err_send;
|
||||
|
||||
if( check_rw_error( device, qrequest ))
|
||||
goto err_send;
|
||||
|
||||
if( device_released_bus( device )) {
|
||||
// device enqueued command, so we have to wait;
|
||||
// in access_finished, we'll ask device whether it wants to
|
||||
// continue some other command
|
||||
bus->active_qrequest = NULL;
|
||||
|
||||
access_finished( bus, device );
|
||||
// we may have rejected commands meanwhile, so tell
|
||||
// the SIM that it can resend them now
|
||||
scsi->cont_send_bus( bus->scsi_cookie );
|
||||
return;
|
||||
}
|
||||
|
||||
//SHOW_ERROR0( 2, "device executes command instantly" );
|
||||
}
|
||||
|
||||
start_dma_wait_no_lock( device, qrequest );
|
||||
|
||||
} else {
|
||||
// on PIO read, we start with waiting, on PIO write we can
|
||||
// transmit data immediately; we let the service thread do
|
||||
// the writing, so the caller can issue the next command
|
||||
// immediately (this optimisation really pays on SMP systems
|
||||
// only)
|
||||
SHOW_FLOW0( 3, "Ready for PIO" );
|
||||
if( qrequest->is_write ) {
|
||||
SHOW_FLOW0( 3, "Scheduling write DPC" );
|
||||
scsi->schedule_dpc( bus->scsi_cookie, bus->irq_dpc, ide_dpc, bus );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_setup:
|
||||
// error during setup
|
||||
if( qrequest->uses_dma )
|
||||
abort_dma( device, qrequest );
|
||||
|
||||
finish_checksense( qrequest );
|
||||
return;
|
||||
|
||||
err_send:
|
||||
// error during/after send;
|
||||
// in this case, the device discards queued request automatically
|
||||
if( qrequest->uses_dma )
|
||||
abort_dma( device, qrequest );
|
||||
|
||||
finish_reset_queue( qrequest );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// check for errors reported by read/write command
|
||||
// return: true, if an error occured
|
||||
bool check_rw_error( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint8 status;
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
if( (status & ide_status_err) != 0 ) {
|
||||
uint8 error;
|
||||
|
||||
if( bus->controller->read_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_error ) != B_OK )
|
||||
{
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
return true;
|
||||
}
|
||||
|
||||
error = device->tf.read.error;
|
||||
|
||||
if( (error & ide_error_icrc) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_CRC );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( qrequest->is_write ) {
|
||||
if( (error & ide_error_wp) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_DATA_PROTECT, SCSIS_ASC_WRITE_PROTECTED );
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if( (error & ide_error_unc) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_UNREC_READ_ERR );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( (error & ide_error_mc) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_MEDIUM_CHANGED );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( (error & ide_error_idnf) != 0 ) {
|
||||
// ID not found - invalid CHS mapping (was: seek error?)
|
||||
set_sense( device, SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_RANDOM_POS_ERROR );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( (error & ide_error_mcr) != 0 ) {
|
||||
// XXX proper sense key?
|
||||
// for TUR this case is not defined !?
|
||||
set_sense( device, SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_REMOVAL_REQUESTED );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( (error & ide_error_nm) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_NOT_READY, SCSIS_ASC_NO_MEDIUM );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( (error & ide_error_abrt) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE );
|
||||
return true;
|
||||
}
|
||||
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check result of ATA command
|
||||
// drdy_required - true if drdy must be set by device
|
||||
// error_mask - bits to be checked in error register
|
||||
// is_write - true, if command was a write command
|
||||
bool check_output( ide_device_info *device,
|
||||
bool drdy_required, int error_mask, bool is_write )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint8 status;
|
||||
|
||||
// check IRQ timeout
|
||||
if( bus->sync_wait_timeout ) {
|
||||
bus->sync_wait_timeout = false;
|
||||
|
||||
device->subsys_status = SCSI_CMD_TIMEOUT;
|
||||
return false;
|
||||
}
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
// if device is busy, other flags are indeterminate
|
||||
if( (status & ide_status_bsy) != 0 ) {
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( drdy_required && ((status & ide_status_drdy) == 0) ) {
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (status & ide_status_err) != 0 ) {
|
||||
uint8 error;
|
||||
|
||||
if( bus->controller->read_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_error ) != B_OK )
|
||||
{
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
return false;
|
||||
}
|
||||
|
||||
error = device->tf.read.error & error_mask;
|
||||
|
||||
if( (error & ide_error_icrc) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_CRC );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( is_write ) {
|
||||
if( (error & ide_error_wp) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_DATA_PROTECT, SCSIS_ASC_WRITE_PROTECTED );
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if( (error & ide_error_unc) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_UNREC_READ_ERR );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( (error & ide_error_mc) != 0 ) {
|
||||
// XXX proper sense key?
|
||||
set_sense( device, SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_MEDIUM_CHANGED );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (error & ide_error_idnf) != 0 ) {
|
||||
// XXX strange error code, don't really know what it means
|
||||
set_sense( device, SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_RANDOM_POS_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (error & ide_error_mcr) != 0 ) {
|
||||
// XXX proper sense key?
|
||||
set_sense( device, SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_REMOVAL_REQUESTED );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (error & ide_error_nm) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_NO_MEDIUM );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (error & ide_error_abrt) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE );
|
||||
return false;
|
||||
}
|
||||
|
||||
// either there was no error bit set or it was masked out
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// execute SET FEATURE command
|
||||
// set subcommand in task file before calling this
|
||||
static bool device_set_feature( ide_device_info *device, int feature )
|
||||
{
|
||||
device->tf_param_mask = ide_mask_features;
|
||||
|
||||
device->tf.write.features = feature;
|
||||
device->tf.write.command = IDE_CMD_SET_FEATURES;
|
||||
|
||||
if( !send_command( device, NULL, true, 1, ide_state_sync_waiting ))
|
||||
return false;
|
||||
|
||||
wait_for_sync( device->bus );
|
||||
|
||||
return check_output( device, true, ide_error_abrt, false );
|
||||
}
|
||||
|
||||
static bool configure_rmsn( ide_device_info *device )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
int i;
|
||||
|
||||
if( !device->infoblock.RMSN_supported ||
|
||||
device->infoblock._127_RMSN_support != 1 )
|
||||
return true;
|
||||
|
||||
if( !device_set_feature( device, IDE_CMD_SET_FEATURES_ENABLE_MSN ))
|
||||
return false;
|
||||
|
||||
bus->controller->read_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_LBA_mid | ide_mask_LBA_high );
|
||||
|
||||
for( i = 0; i < 5; ++i ) {
|
||||
// don't use TUR as it checks not ide_error_mcr | ide_error_mc | ide_error_wp
|
||||
// but: we don't check wp as well
|
||||
device->combined_sense = 0;
|
||||
|
||||
device->tf_param_mask = 0;
|
||||
device->tf.write.command = IDE_CMD_GET_MEDIA_STATUS;
|
||||
|
||||
if( !send_command( device, NULL, true, 15, ide_state_sync_waiting ))
|
||||
continue;
|
||||
|
||||
if( check_output( device, true,
|
||||
ide_error_nm | ide_error_abrt | ide_error_mcr | ide_error_mc,
|
||||
true )
|
||||
|| decode_sense_asc_ascq( device->combined_sense ) == SCSIS_ASC_NO_MEDIUM )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
configure_command_queueing(ide_device_info *device)
|
||||
{
|
||||
device->CQ_enabled = device->CQ_supported = false;
|
||||
|
||||
if (!device->bus->can_CQ
|
||||
|| !device->infoblock.DMA_QUEUED_supported)
|
||||
return initialize_qreq_array(device, 1);
|
||||
|
||||
if (device->infoblock.RELEASE_irq_supported
|
||||
&& !device_set_feature( device, IDE_CMD_SET_FEATURES_DISABLE_REL_INT))
|
||||
dprintf("Cannot disable release irq\n");
|
||||
|
||||
if (device->infoblock.SERVICE_irq_supported
|
||||
&& !device_set_feature(device, IDE_CMD_SET_FEATURES_DISABLE_SERV_INT))
|
||||
dprintf("Cannot disable service irq\n");
|
||||
|
||||
device->CQ_enabled = device->CQ_supported = true;
|
||||
|
||||
SHOW_INFO0(2, "Enabled command queueing");
|
||||
|
||||
// official IBM docs talk about 31 queue entries, though
|
||||
// their disks report 32; let's hope their docs are wrong
|
||||
return initialize_qreq_array(device, device->infoblock.queue_depth + 1);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
prep_ata(ide_device_info *device)
|
||||
{
|
||||
ide_device_infoblock *infoblock = &device->infoblock;
|
||||
uint32 chs_capacity;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
device->is_atapi = false;
|
||||
device->exec_io = ata_exec_io;
|
||||
device->last_lun = 0;
|
||||
|
||||
// warning: ata == 0 means "this is ata"...
|
||||
if (infoblock->_0.ata.ATA != 0) {
|
||||
// CF has either magic header or CFA bit set
|
||||
// we merge it to "CFA bit set" for easier (later) testing
|
||||
if (*(uint16 *)infoblock == 0x848a)
|
||||
infoblock->CFA_supported = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "1" );
|
||||
|
||||
if (!infoblock->_54_58_valid) {
|
||||
// normally, current_xxx contains active CHS mapping,
|
||||
// but if BIOS didn't call INITIALIZE DEVICE PARAMETERS
|
||||
// the default mapping is used
|
||||
infoblock->current_sectors = infoblock->sectors;
|
||||
infoblock->current_cylinders = infoblock->cylinders;
|
||||
infoblock->current_heads = infoblock->heads;
|
||||
}
|
||||
|
||||
// just in case capacity_xxx isn't initialized - calculate it manually
|
||||
// (seems that this information is really redundant; hopefully)
|
||||
chs_capacity = infoblock->current_sectors * infoblock->current_cylinders *
|
||||
infoblock->current_heads;
|
||||
|
||||
infoblock->capacity_low = chs_capacity & 0xff;
|
||||
infoblock->capacity_high = chs_capacity >> 8;
|
||||
|
||||
// checking LBA_supported flag should be sufficient, but it seems
|
||||
// that checking LBA_total_sectors is a good idea
|
||||
device->use_LBA = infoblock->LBA_supported && infoblock->LBA_total_sectors != 0;
|
||||
|
||||
if (device->use_LBA) {
|
||||
device->total_sectors = infoblock->LBA_total_sectors;
|
||||
device->tf.lba.mode = ide_mode_lba;
|
||||
} else {
|
||||
device->total_sectors = chs_capacity;
|
||||
device->tf.chs.mode = ide_mode_chs;
|
||||
}
|
||||
|
||||
device->use_48bits = infoblock->_48_bit_addresses_supported;
|
||||
|
||||
if (device->use_48bits)
|
||||
device->total_sectors = infoblock->LBA48_total_sectors;
|
||||
|
||||
SHOW_FLOW0(3, "2");
|
||||
|
||||
if (!configure_dma(device)
|
||||
|| !configure_command_queueing(device)
|
||||
|| !configure_rmsn(device))
|
||||
return false;
|
||||
|
||||
SHOW_FLOW0(3, "3");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
enable_CQ(ide_device_info *device, bool enable)
|
||||
{
|
||||
}
|
520
src/add-ons/kernel/bus_managers/ide/atapi.c
Normal file
520
src/add-ons/kernel/bus_managers/ide/atapi.c
Normal file
@ -0,0 +1,520 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
ATAPI command protocol
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
|
||||
#include <bus/scsi/scsi_cmds.h>
|
||||
|
||||
#include "ide_cmds.h"
|
||||
#include "ide_sim.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// used for MODE SENSE/SELECT 6 emulation; maximum size is 255 + header,
|
||||
// so this is a safe bet
|
||||
#define IDE_ATAPI_BUFFER_SIZE 512
|
||||
|
||||
|
||||
// set sense according to error reported by device
|
||||
// return: true - device reported error
|
||||
static bool check_packet_error( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
int status;
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
if( (status & (ide_status_err | ide_status_df)) != 0 ) {
|
||||
int error;
|
||||
|
||||
SHOW_FLOW( 3, "packet error, status=%02x", status );
|
||||
|
||||
if( bus->controller->read_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_error ) != B_OK )
|
||||
{
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
return true;
|
||||
}
|
||||
|
||||
// the upper 4 bits contain sense key
|
||||
// we don't want to clutter syslog with "not ready" and UA messages,
|
||||
// so use FLOW messages for them
|
||||
error = device->tf.read.error;
|
||||
if( (error >> 4) == SCSIS_KEY_NOT_READY ||
|
||||
(error >> 4) == SCSIS_KEY_UNIT_ATTENTION )
|
||||
SHOW_FLOW( 3, "error=%x", error );
|
||||
else
|
||||
SHOW_ERROR( 3, "error=%x", error );
|
||||
|
||||
// ATAPI says that
|
||||
// "ABRT shall be set to one if the requested command has been command
|
||||
// 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
|
||||
// action requested by the command."
|
||||
// Effectively, it can be set if "something goes wrong", including
|
||||
// if the medium got changed. Therefore, we currently ignore the bit
|
||||
// and rely on auto-sense information
|
||||
/*
|
||||
if( (error & ide_error_abrt) != 0 ) {
|
||||
// if command got aborted, there's no point in reading sense
|
||||
set_sense( device, SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE );
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// tell SCSI layer that sense must be requested
|
||||
// (we don't take care of auto-sense ourselve)
|
||||
device->subsys_status = SCSI_REQ_CMP_ERR;
|
||||
qrequest->request->device_status = SCSI_STATUS_CHECK_CONDITION;
|
||||
// reset pending emulated sense - its overwritten by a real one
|
||||
device->combined_sense = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// IRQ handler of packet transfer (executed as DPC)
|
||||
void packet_dpc( ide_qrequest *qrequest )
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
ide_bus_info *bus = device->bus;
|
||||
int status;
|
||||
uint32 timeout = qrequest->request->timeout > 0 ?
|
||||
qrequest->request->timeout : IDE_STD_TIMEOUT;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
bus->controller->read_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_error | ide_mask_ireason );
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
if( qrequest->packet_irq ) {
|
||||
// device requests packet
|
||||
qrequest->packet_irq = false;
|
||||
|
||||
if( !device->tf.packet_res.cmd_or_data ||
|
||||
device->tf.packet_res.input_or_output ||
|
||||
(status & ide_status_drq) == 0 )
|
||||
{
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
start_waiting_nolock( device->bus, timeout, ide_state_async_waiting );
|
||||
|
||||
// send packet
|
||||
if( bus->controller->write_pio( bus->channel,
|
||||
(uint16 *)device->packet, sizeof( device->packet ) / sizeof( uint16 ),
|
||||
true ) != B_OK )
|
||||
{
|
||||
SHOW_ERROR0( 1, "Error sending command packet" );
|
||||
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
goto err_cancel_timer;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( qrequest->uses_dma ) {
|
||||
// DMA transmission finished
|
||||
bool dma_err, dev_err;
|
||||
|
||||
// don't check drq - if there is some data left, we cannot handle
|
||||
// it anyway
|
||||
// XXX does the device throw remaining data away on DMA overflow?
|
||||
SHOW_FLOW0( 3, "DMA done" );
|
||||
|
||||
dma_err = !finish_dma( device );
|
||||
dev_err = check_packet_error( device, qrequest );
|
||||
|
||||
// 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
|
||||
// device error, so we ignore the dma error and use the device error instead
|
||||
if( dev_err ) {
|
||||
finish_checksense( qrequest );
|
||||
return;
|
||||
}
|
||||
|
||||
// device is happy, let's see what the controller says
|
||||
if( !dma_err ) {
|
||||
// if DMA works, reset error counter so we don't disable
|
||||
// DMA only because it didn't work once in a while
|
||||
device->DMA_failures = 0;
|
||||
// this is a lie, but there is no way to find out
|
||||
// how much has been transmitted
|
||||
qrequest->request->data_resid = 0;
|
||||
finish_checksense( qrequest );
|
||||
|
||||
} else {
|
||||
// DMA transmission went wrong
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE );
|
||||
|
||||
if( ++device->DMA_failures >= MAX_DMA_FAILURES ) {
|
||||
SHOW_ERROR0( 1, "Disabling DMA because of too many errors" );
|
||||
|
||||
device->DMA_enabled = false;
|
||||
}
|
||||
|
||||
finish_checksense( qrequest );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// PIO mode
|
||||
if( (status & ide_status_drq) != 0 ) {
|
||||
// device wants to transmit data
|
||||
int length;
|
||||
status_t err;
|
||||
|
||||
SHOW_FLOW0( 3, "data transmission" );
|
||||
|
||||
if( device->tf.packet_res.cmd_or_data ) {
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
// check whether transmission direction matches
|
||||
if( (device->tf.packet_res.input_or_output ^ qrequest->is_write) == 0 ) {
|
||||
SHOW_ERROR0( 2, "data transmission in wrong way!?" );
|
||||
|
||||
// XXX hm, either the device is broken or the caller has specified
|
||||
// the wrong direction - what is the proper handling?
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_FAILURE );
|
||||
|
||||
// reset device to make it alive
|
||||
// TBD: the device will abort next command with a reset condition
|
||||
// perhaps we should hide that by reading sense?
|
||||
SHOW_FLOW0( 3, "Reset" );
|
||||
reset_device( device, qrequest );
|
||||
|
||||
finish_checksense( qrequest );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// ask device how much data it wants to transmit
|
||||
bus->controller->read_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_byte_count );
|
||||
|
||||
length = device->tf.packet_res.byte_count_0_7 |
|
||||
((int)device->tf.packet_res.byte_count_8_15 << 8);
|
||||
|
||||
SHOW_FLOW( 3, "device transmittes %d bytes", length );
|
||||
|
||||
// start waiting before starting transmission, else we
|
||||
// could start waiting too late;
|
||||
// don't mind getting overtaken by IRQ handler - as it will
|
||||
// issue a DPC for the thread context we are in, we are save
|
||||
start_waiting_nolock( device->bus, timeout, ide_state_async_waiting );
|
||||
|
||||
if( device->tf.packet_res.input_or_output )
|
||||
err = read_PIO_block( qrequest, length );
|
||||
else
|
||||
err = write_PIO_block( qrequest, length );
|
||||
|
||||
// only report "real" errors;
|
||||
// discarding data (ERR_TOO_BIG) can happen but is OK
|
||||
if( err == B_ERROR ) {
|
||||
SHOW_ERROR0( 2, "Error during PIO transmission" );
|
||||
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
goto err_cancel_timer;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "7" );
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
// device has done job and doesn't want to transmit data anymore
|
||||
// -> finish request
|
||||
SHOW_FLOW0( 3, "no data" );
|
||||
|
||||
check_packet_error( device, qrequest );
|
||||
|
||||
SHOW_FLOW( 3, "finished: %d of %d left",
|
||||
(int)qrequest->request->data_resid,
|
||||
(int)qrequest->request->data_len );
|
||||
|
||||
finish_checksense( qrequest );
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_cancel_timer:
|
||||
cancel_irq_timeout( device->bus );
|
||||
|
||||
err:
|
||||
finish_checksense( qrequest );
|
||||
}
|
||||
|
||||
|
||||
// create taskfile for ATAPI packet
|
||||
static bool create_packet_taskfile( ide_device_info *device, ide_qrequest *qrequest,
|
||||
bool write )
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
|
||||
SHOW_FLOW( 3, "DMA enabled=%d, uses_dma=%d, scsi_cmd=%x",
|
||||
device->DMA_enabled, qrequest->uses_dma, device->packet[0] );
|
||||
|
||||
device->tf_param_mask = ide_mask_features | ide_mask_byte_count;
|
||||
|
||||
device->tf.packet.dma = qrequest->uses_dma;
|
||||
device->tf.packet.ovl = 0;
|
||||
device->tf.packet.byte_count_0_7 = request->data_len & 0xff;
|
||||
device->tf.packet.byte_count_8_15 = request->data_len >> 8;
|
||||
device->tf.packet.command = IDE_CMD_PACKET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// send ATAPI packet
|
||||
void send_packet( ide_device_info *device, ide_qrequest *qrequest, bool write )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
bool packet_irq = device->atapi.packet_irq;
|
||||
uint8 scsi_cmd = device->packet[0];
|
||||
|
||||
SHOW_FLOW( 3, "qrequest=%p, command=%x", qrequest, scsi_cmd );
|
||||
|
||||
/*{
|
||||
unsigned int i;
|
||||
|
||||
for( i = 0; i < sizeof( device->packet ); ++i )
|
||||
dprintf( "%x ", device->packet[i] );
|
||||
}*/
|
||||
|
||||
SHOW_FLOW( 3, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x (len=%d)",
|
||||
device->packet[0], device->packet[1], device->packet[2],
|
||||
device->packet[3], device->packet[4], device->packet[5],
|
||||
device->packet[6], device->packet[7], device->packet[8],
|
||||
device->packet[9], device->packet[10], device->packet[11],
|
||||
qrequest->request->cdb_len );
|
||||
|
||||
//snooze( 1000000 );
|
||||
|
||||
qrequest->is_write = write;
|
||||
// if needed, mark first IRQ as being packet request IRQ
|
||||
qrequest->packet_irq = packet_irq;
|
||||
|
||||
// only READ/WRITE commands can use DMA
|
||||
// (the device may support it always, but IDE controllers don't
|
||||
// report how much data is transmitted, and this information is
|
||||
// crucial for the SCSI protocol)
|
||||
// special offer: let READ_CD commands use DMA too
|
||||
qrequest->uses_dma = device->DMA_enabled &&
|
||||
(scsi_cmd == SCSI_OP_READ_6 || scsi_cmd == SCSI_OP_WRITE_6 ||
|
||||
scsi_cmd == SCSI_OP_READ_10 || scsi_cmd == SCSI_OP_WRITE_10 ||
|
||||
scsi_cmd == SCSI_OP_READ_12 || scsi_cmd == SCSI_OP_WRITE_12 ||
|
||||
scsi_cmd == SCSI_OP_READ_CD );
|
||||
|
||||
// try preparing DMA, if that fails, fall back to PIO
|
||||
if( qrequest->uses_dma ) {
|
||||
SHOW_FLOW0( 3, "0" );
|
||||
if( !prepare_dma( device, qrequest ))
|
||||
qrequest->uses_dma = false;
|
||||
|
||||
SHOW_FLOW( 3, "0->%d", qrequest->uses_dma );
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "1" );
|
||||
|
||||
if( !qrequest->uses_dma )
|
||||
prep_PIO_transfer( device, qrequest );
|
||||
|
||||
SHOW_FLOW0( 3, "2" );
|
||||
|
||||
if( !create_packet_taskfile( device, qrequest, write ))
|
||||
goto err_setup;
|
||||
|
||||
SHOW_FLOW0( 3, "3" );
|
||||
|
||||
if( !send_command( device, qrequest, false,
|
||||
device->atapi.packet_irq_timeout,
|
||||
device->atapi.packet_irq ? ide_state_async_waiting : ide_state_accessing ))
|
||||
goto err_setup;
|
||||
|
||||
SHOW_FLOW0( 3, "4" );
|
||||
|
||||
if( packet_irq )
|
||||
// device asks for packet via IRQ;
|
||||
// timeout and stuff is already set by send_command
|
||||
return;
|
||||
|
||||
SHOW_FLOW0( 3, "5" );
|
||||
|
||||
// wait for device to get ready for packet transmission
|
||||
if( !ide_wait( device, ide_status_drq, ide_status_bsy, false, 100000 ))
|
||||
goto err_setup;
|
||||
|
||||
SHOW_FLOW0( 3, "6" );
|
||||
|
||||
// make sure device really asks for command packet
|
||||
bus->controller->read_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_ireason );
|
||||
|
||||
if( !device->tf.packet_res.cmd_or_data ||
|
||||
device->tf.packet_res.input_or_output )
|
||||
{
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
goto err_setup;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "7" );
|
||||
|
||||
// some old drives need a delay before submitting the packet
|
||||
spin( 10 );
|
||||
|
||||
// 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
|
||||
// Condition error). Thus, we have to lock out the IRQ handler _before_ we
|
||||
// start packet transmission, which forbids all kind of interrupts for some
|
||||
// time; to reduce this period, blocking is done just before last dword is
|
||||
// sent (avoid sending 16 bits as controller may transmit 32 bit chunks)
|
||||
|
||||
// write packet
|
||||
if( bus->controller->write_pio( bus->channel,
|
||||
(uint16 *)device->packet, sizeof( device->packet ) / sizeof( uint16 ) - 2,
|
||||
true ) != B_OK )
|
||||
{
|
||||
goto err_packet;
|
||||
}
|
||||
|
||||
IDE_LOCK( bus );
|
||||
|
||||
if( bus->controller->write_pio( bus->channel,
|
||||
(uint16 *)device->packet + sizeof( device->packet ) / sizeof( uint16 ) - 2,
|
||||
2, true ) != B_OK )
|
||||
{
|
||||
goto err_packet2;
|
||||
}
|
||||
|
||||
if( qrequest->uses_dma ) {
|
||||
SHOW_FLOW0( 3, "ready for DMA" );
|
||||
|
||||
// S/G table must already be setup - we hold the bus lock, so
|
||||
// we really have to hurry up
|
||||
start_dma_wait( device, qrequest );
|
||||
} else {
|
||||
uint32 timeout = qrequest->request->timeout > 0 ?
|
||||
qrequest->request->timeout : IDE_STD_TIMEOUT;
|
||||
|
||||
start_waiting( bus, timeout, ide_state_async_waiting );
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "8" );
|
||||
|
||||
return;
|
||||
|
||||
err_packet2:
|
||||
IDE_UNLOCK( bus );
|
||||
|
||||
err_packet:
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
|
||||
err_setup:
|
||||
if( qrequest->uses_dma )
|
||||
abort_dma( device, qrequest );
|
||||
|
||||
finish_checksense( qrequest );
|
||||
}
|
||||
|
||||
|
||||
// execute SCSI I/O for atapi devices
|
||||
void atapi_exec_io( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
|
||||
SHOW_FLOW( 3, "command=%x", qrequest->request->cdb[0] );
|
||||
|
||||
// ATAPI command packets are 12 bytes long;
|
||||
// if the command is shorter, remaining bytes must be padded with zeros
|
||||
memset( device->packet, 0, sizeof( device->packet ));
|
||||
memcpy( device->packet, request->cdb, request->cdb_len );
|
||||
|
||||
if( request->cdb[0] == SCSI_OP_REQUEST_SENSE && device->combined_sense ) {
|
||||
// we have a pending emulated sense - return it on REQUEST SENSE
|
||||
ide_request_sense( device, qrequest );
|
||||
finish_checksense( qrequest );
|
||||
|
||||
} else {
|
||||
// reset all error codes for new request
|
||||
start_request( device, qrequest );
|
||||
|
||||
// now we have an IDE packet
|
||||
send_packet( device, qrequest,
|
||||
(request->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** prepare device info for ATAPI device */
|
||||
|
||||
bool
|
||||
prep_atapi(ide_device_info *device)
|
||||
{
|
||||
ide_device_infoblock *infoblock = &device->infoblock;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
device->is_atapi = true;
|
||||
device->exec_io = atapi_exec_io;
|
||||
|
||||
if (infoblock->_0.atapi.ATAPI != 2)
|
||||
return false;
|
||||
|
||||
switch(infoblock->_0.atapi.drq_speed) {
|
||||
case 0:
|
||||
case 2:
|
||||
device->atapi.packet_irq = false;
|
||||
break;
|
||||
case 1:
|
||||
device->atapi.packet_irq = true;
|
||||
device->atapi.packet_irq_timeout = IDE_STD_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
SHOW_FLOW(3, "drq speed: %d", infoblock->_0.atapi.drq_speed);
|
||||
|
||||
/*if( infoblock->_0.atapi.packet_size != 0 )
|
||||
return false;*/
|
||||
|
||||
device->device_type = infoblock->_0.atapi.type;
|
||||
device->last_lun = infoblock->last_lun;
|
||||
|
||||
SHOW_FLOW(3, "device_type=%d, last_lun=%d",
|
||||
device->device_type, device->last_lun);
|
||||
|
||||
// don't use task file to select LUN but command packet
|
||||
// (SCSI bus manager sets LUN there automatically)
|
||||
device->tf.packet.lun = 0;
|
||||
|
||||
if (!initialize_qreq_array(device, 1)
|
||||
|| !configure_dma(device))
|
||||
return false;
|
||||
|
||||
// currently, we don't support queuing, but I haven't found any
|
||||
// ATAPI device that supports queuing anyway, so this is no loss
|
||||
device->CQ_enabled = device->CQ_supported = false;
|
||||
|
||||
return true;
|
||||
}
|
450
src/add-ons/kernel/bus_managers/ide/basic_protocol.c
Normal file
450
src/add-ons/kernel/bus_managers/ide/basic_protocol.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Basic ATA/ATAPI protocol functions
|
||||
*/
|
||||
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include <bus/scsi/scsi_cmds.h>
|
||||
|
||||
#include "ide_sim.h"
|
||||
#include "ide_cmds.h"
|
||||
|
||||
|
||||
// time in µs an IDE interrupt may get delayed
|
||||
// as this is used for waiting in normal code, this applies to hardware delays only
|
||||
// it's used for a hardware bug fix as well, see send_command
|
||||
#define MAX_IRQ_DELAY 50
|
||||
|
||||
// maximum number send tries before giving up
|
||||
#define MAX_FAILED_SEND 1
|
||||
|
||||
// busy-wait for data request going high
|
||||
bool wait_for_drq( ide_device_info *device )
|
||||
{
|
||||
return ide_wait( device, ide_status_drq, 0, true, 10000000 );
|
||||
}
|
||||
|
||||
|
||||
// busy-wait for data request going low
|
||||
bool wait_for_drqdown( ide_device_info *device )
|
||||
{
|
||||
return ide_wait( device, 0, ide_status_drq, true, 1000000 );
|
||||
}
|
||||
|
||||
|
||||
// busy-wait for device ready
|
||||
bool wait_for_drdy( ide_device_info *device )
|
||||
{
|
||||
return ide_wait( device, ide_status_drdy, ide_status_bsy, false, 5000000 );
|
||||
}
|
||||
|
||||
|
||||
// reset entire IDE bus
|
||||
// all active request apart from <ignore> are resubmitted
|
||||
bool reset_bus( ide_device_info *device, ide_qrequest *ignore )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
ide_controller_interface *controller = bus->controller;
|
||||
ide_channel_cookie channel = bus->channel;
|
||||
|
||||
FAST_LOG0( bus->log, ev_ide_reset_bus );
|
||||
|
||||
if( device->reconnect_timer_installed ) {
|
||||
cancel_timer( &device->reconnect_timer.te );
|
||||
device->reconnect_timer_installed = false;
|
||||
}
|
||||
|
||||
if( device->other_device->reconnect_timer_installed ) {
|
||||
cancel_timer( &device->other_device->reconnect_timer.te );
|
||||
device->other_device->reconnect_timer_installed = false;
|
||||
}
|
||||
|
||||
// activate srst signal for 5 µs
|
||||
// also, deactivate IRQ
|
||||
// (as usual, we will get an IRQ on disabling, but as we leave them
|
||||
// disabled for 2 ms, this false report is ignored)
|
||||
if( controller->write_device_control( channel,
|
||||
ide_devctrl_nien | ide_devctrl_srst | ide_devctrl_bit3 ) != B_OK )
|
||||
goto err0;
|
||||
|
||||
spin( 5 );
|
||||
|
||||
if( controller->write_device_control( channel,
|
||||
ide_devctrl_nien | ide_devctrl_bit3 ) != B_OK )
|
||||
goto err0;
|
||||
|
||||
// let devices wake up
|
||||
snooze( 2000 );
|
||||
|
||||
// ouch, we have to wait up to 31 seconds!
|
||||
if( !ide_wait( device, 0, ide_status_bsy, true, 31000000 )) {
|
||||
|
||||
// as we don't know which of the devices is broken
|
||||
// we leave them both alive
|
||||
if( controller->write_device_control( channel, ide_devctrl_bit3 ) != B_OK )
|
||||
goto err0;
|
||||
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_TIMEOUT );
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if( controller->write_device_control( channel, ide_devctrl_bit3 ) != B_OK )
|
||||
goto err0;
|
||||
|
||||
finish_all_requests( bus->devices[0], ignore, SCSI_SCSI_BUS_RESET, true );
|
||||
finish_all_requests( bus->devices[1], ignore, SCSI_SCSI_BUS_RESET, true );
|
||||
|
||||
return true;
|
||||
|
||||
err0:
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
|
||||
err1:
|
||||
finish_all_requests( bus->devices[0], ignore, SCSI_SCSI_BUS_RESET, true );
|
||||
finish_all_requests( bus->devices[1], ignore, SCSI_SCSI_BUS_RESET, true );
|
||||
|
||||
//xpt->call_async( bus->xpt_cookie, -1, -1, AC_BUS_RESET, NULL, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// execute packet device reset.
|
||||
// resets entire bus on fail or if device is not atapi;
|
||||
// all requests but <ignore> are resubmitted
|
||||
bool reset_device( ide_device_info *device, ide_qrequest *ignore )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
status_t res;
|
||||
uint8 orig_command;
|
||||
|
||||
FAST_LOG1( bus->log, ev_ide_reset_device, device->is_device1 );
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
if( !device->is_atapi )
|
||||
goto err;
|
||||
|
||||
if( device->reconnect_timer_installed ) {
|
||||
cancel_timer( &device->reconnect_timer.te );
|
||||
device->reconnect_timer_installed = false;
|
||||
}
|
||||
|
||||
// select device
|
||||
if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_device_head ) != B_OK )
|
||||
goto err;
|
||||
|
||||
// safe original command to let caller restart it
|
||||
orig_command = device->tf.write.command;
|
||||
|
||||
// send device reset, independ of current device state
|
||||
// (that's the point of a reset)
|
||||
device->tf.write.command = IDE_CMD_DEVICE_RESET;
|
||||
res = bus->controller->write_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_command );
|
||||
device->tf.write.command = orig_command;
|
||||
|
||||
if( res != B_OK )
|
||||
goto err;
|
||||
|
||||
// don't know how long to wait, but 31 seconds, like soft reset,
|
||||
// should be enough
|
||||
if( !ide_wait( device, 0, ide_status_bsy, true, 31000000 ))
|
||||
goto err;
|
||||
|
||||
// alright, resubmit all requests
|
||||
finish_all_requests( device, ignore, SCSI_SCSI_BUS_RESET, true );
|
||||
|
||||
SHOW_FLOW0( 3, "done" );
|
||||
return true;
|
||||
|
||||
err:
|
||||
// do the hard way
|
||||
return reset_bus( device, ignore );
|
||||
}
|
||||
|
||||
// new_state must be either accessing, async_waiting or sync_waiting
|
||||
// param_mask must not include command register
|
||||
bool send_command( ide_device_info *device, ide_qrequest *qrequest,
|
||||
bool need_drdy, uint32 timeout, ide_bus_state new_state )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
bigtime_t irq_disabled_at = 0; // make compiler happy
|
||||
uint8 num_retries = 0;
|
||||
bool irq_guard;
|
||||
|
||||
FAST_LOGN( bus->log, ev_ide_send_command, 15, device->is_device1, (uint32)qrequest,
|
||||
device->tf.raw.r[0], device->tf.raw.r[1], device->tf.raw.r[2],
|
||||
device->tf.raw.r[3], device->tf.raw.r[4], device->tf.raw.r[5],
|
||||
device->tf.raw.r[6],
|
||||
device->tf.raw.r[7], device->tf.raw.r[8], device->tf.raw.r[9],
|
||||
device->tf.raw.r[10], device->tf.raw.r[11] );
|
||||
|
||||
retry:
|
||||
irq_guard = bus->num_running_reqs > 1;
|
||||
|
||||
SHOW_FLOW( 3, "qrequest=%p, request=%p", qrequest,
|
||||
qrequest ? qrequest->request : NULL );
|
||||
|
||||
// if there are pending requests, IRQs must be disabled to
|
||||
// not mix up IRQ reasons
|
||||
// XXX can we avoid that with the IDE_LOCK trick? It would
|
||||
// save some work and the bug workaround!
|
||||
if( irq_guard ) {
|
||||
if( bus->controller->write_device_control( bus->channel,
|
||||
ide_devctrl_nien | ide_devctrl_bit3 ) != B_OK )
|
||||
goto err;
|
||||
|
||||
irq_disabled_at = system_time();
|
||||
}
|
||||
|
||||
// select device
|
||||
if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_device_head ) != B_OK )
|
||||
goto err;
|
||||
|
||||
bus->active_device = device;
|
||||
|
||||
if( !ide_wait( device, 0, ide_status_bsy | ide_status_drq, false, 50000 )) {
|
||||
uint8 status;
|
||||
|
||||
SHOW_FLOW0( 1, "device is not ready" );
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
if( status == 0xff ) {
|
||||
// there is no device (should happen during detection only)
|
||||
SHOW_FLOW0( 1, "there is no device" );
|
||||
|
||||
// device detection recognizes this code as "all hope lost", so
|
||||
// neither replace it nor use it anywhere else
|
||||
device->subsys_status = SCSI_TID_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset device and retry
|
||||
if( reset_device( device, qrequest ) && ++num_retries <= MAX_FAILED_SEND ) {
|
||||
SHOW_FLOW0( 1, "retrying" );
|
||||
goto retry;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 1, "giving up" );
|
||||
|
||||
// reset to often - abort request
|
||||
device->subsys_status = SCSI_SEL_TIMEOUT;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( need_drdy &&
|
||||
(bus->controller->get_altstatus( bus->channel ) & ide_status_drdy) == 0 )
|
||||
{
|
||||
SHOW_FLOW0( 3, "drdy not set" );
|
||||
device->subsys_status = SCSI_SEQUENCE_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// write parameters
|
||||
if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
|
||||
device->tf_param_mask ) != B_OK )
|
||||
goto err;
|
||||
|
||||
if( irq_guard ) {
|
||||
// IRQ may be fired by service requests and by the process of disabling(!)
|
||||
// them (I heard this is caused by edge triggered PCI IRQs)
|
||||
|
||||
// wait at least 50 µs to catch all pending irq's
|
||||
// (at my system, up to 30 µs elapsed)
|
||||
|
||||
// additionally, old drives (at least my IBM-DTTA-351010) loose
|
||||
// sync if they are pushed too hard - on heavy overlapped write
|
||||
// stress this drive tends to forget outstanding requests,
|
||||
// waiting at least 50 µs seems(!) to solve this
|
||||
while( system_time() - irq_disabled_at < MAX_IRQ_DELAY )
|
||||
spin( 1 );
|
||||
|
||||
}
|
||||
|
||||
// if we will start waiting once the command is sent, we have to
|
||||
// lock the bus before sending; this way, IRQs that are fired
|
||||
// shortly before/after sending of command are delayed until the
|
||||
// command is really sent (start_waiting unlocks the bus) and then
|
||||
// the IRQ handler can check savely whether the IRQ really signals
|
||||
// finishing of command or not by testing the busy-signal of the device
|
||||
if( new_state != ide_state_accessing ) {
|
||||
IDE_LOCK( bus );
|
||||
}
|
||||
|
||||
if( irq_guard ) {
|
||||
// now it's clear why IRQs gets fired, so we can enable them again
|
||||
if( bus->controller->write_device_control( bus->channel,
|
||||
ide_devctrl_bit3 ) != B_OK )
|
||||
goto err1;
|
||||
}
|
||||
|
||||
// write command code - this will start the actual command
|
||||
SHOW_FLOW( 3, "Writing command 0x%02x", (int)device->tf.write.command );
|
||||
if( bus->controller->write_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_command ) != B_OK )
|
||||
goto err1;
|
||||
|
||||
// start waiting now; also un-blocks IRQ handler (see above)
|
||||
if( new_state != ide_state_accessing ) {
|
||||
start_waiting( bus, timeout, new_state );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err1:
|
||||
if( timeout > 0 ) {
|
||||
bus->state = ide_state_accessing;
|
||||
IDE_UNLOCK( bus );
|
||||
}
|
||||
|
||||
err:
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// busy-wait for device
|
||||
// mask - bits of status register that must be set
|
||||
// not_mask - bits of status register that must not be set
|
||||
// check_err - abort if error bit is set
|
||||
// timeout - waiting timeout
|
||||
// return: true on success
|
||||
bool ide_wait( ide_device_info *device, int mask, int not_mask,
|
||||
bool check_err, bigtime_t timeout )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
bigtime_t start_time = system_time();
|
||||
|
||||
while( 1 ) {
|
||||
bigtime_t elapsed_time;
|
||||
int status;
|
||||
|
||||
// do spin before test as the device needs 400 ns
|
||||
// to update its status register
|
||||
spin( 1 );
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
if( (status & mask) == mask &&
|
||||
(status & not_mask) == 0 )
|
||||
return true;
|
||||
|
||||
if( check_err && (status & ide_status_err) != 0 ) {
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return false;
|
||||
}
|
||||
|
||||
elapsed_time = system_time() - start_time;
|
||||
|
||||
if( elapsed_time > timeout ) {
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_TIMEOUT );
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we've waited more then 5ms, we start passive waiting
|
||||
// to reduce system load
|
||||
if( elapsed_time > 5000 )
|
||||
snooze( elapsed_time / 10 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tell device to continue queued command
|
||||
// on return, no waiting is active!
|
||||
// tag - will contain tag of command to be continued
|
||||
// return: true - request continued
|
||||
// false - something went wrong; sense set
|
||||
bool device_start_service( ide_device_info *device, int *tag )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
FAST_LOG1( bus->log, ev_ide_device_start_service, device->is_device1 );
|
||||
|
||||
device->tf.write.command = IDE_CMD_SERVICE;
|
||||
device->tf.queued.mode = ide_mode_lba;
|
||||
|
||||
if( bus->active_device != device ) {
|
||||
// don't apply any precautions in terms of IRQ
|
||||
// -> the bus is in accessing state, so IRQs are ignored anyway
|
||||
if( bus->controller->write_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_device_head ) != B_OK )
|
||||
// on error, pretend that this device asks for service
|
||||
// -> the disappeared controller will be recognized soon ;)
|
||||
return true;
|
||||
|
||||
bus->active_device = device;
|
||||
|
||||
// give one clock (400 ns) to take notice
|
||||
spin( 1 );
|
||||
}
|
||||
|
||||
// here we go...
|
||||
if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_command ) != B_OK )
|
||||
goto err;
|
||||
|
||||
// we need to wait for the device as we want to read the tag
|
||||
if( !ide_wait( device, ide_status_drdy, ide_status_bsy, false, 1000000 ))
|
||||
return false;
|
||||
|
||||
// read tag
|
||||
if( bus->controller->read_command_block_regs( bus->channel, &device->tf,
|
||||
ide_mask_sector_count ) != B_OK )
|
||||
goto err;
|
||||
|
||||
if( device->tf.queued.release ) {
|
||||
// bus release is the wrong answer to a service request
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return false;
|
||||
}
|
||||
|
||||
*tag = device->tf.queued.tag;
|
||||
|
||||
FAST_LOG2( bus->log, ev_ide_device_start_service2, device->is_device1, *tag );
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check device whether it wants to continue queued request
|
||||
bool check_service_req( ide_device_info *device )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
int status;
|
||||
|
||||
// fast bailout if there is no request pending
|
||||
if( device->num_running_reqs == 0 )
|
||||
return false;
|
||||
|
||||
if( bus->active_device != device ) {
|
||||
// don't apply any precautions in terms of IRQ
|
||||
// -> the bus is in accessing state, so IRQs are ignored anyway
|
||||
if( bus->controller->write_command_block_regs( bus->channel,
|
||||
&device->tf, ide_mask_device_head ) != B_OK )
|
||||
// on error, pretend that this device asks for service
|
||||
// -> the disappeared controller will be recognized soon ;)
|
||||
return true;
|
||||
|
||||
bus->active_device = device;
|
||||
|
||||
// give one clock (400 ns) to take notice
|
||||
spin( 1 );
|
||||
}
|
||||
|
||||
status = bus->controller->get_altstatus( bus->channel );
|
||||
|
||||
return (status & ide_status_service) != 0;
|
||||
}
|
||||
|
127
src/add-ons/kernel/bus_managers/ide/channels.c
Normal file
127
src/add-ons/kernel/bus_managers/ide/channels.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Manager of IDE controllers
|
||||
|
||||
Whenever a new IDE channel is reported, a new SIM is
|
||||
registered at the SCSI bus manager.
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <blkman.h>
|
||||
|
||||
|
||||
/** called when an IDE channel was registered by a controller driver */
|
||||
|
||||
static status_t
|
||||
ide_channel_added(pnp_node_handle parent)
|
||||
{
|
||||
char *str = NULL, *controller_name = NULL;
|
||||
uint32 channel_id;
|
||||
|
||||
SHOW_FLOW0( 2, "" );
|
||||
|
||||
if (pnp->get_attr_string(parent, PNP_DRIVER_TYPE, &str, false) != B_OK
|
||||
|| strcmp(str, IDE_BUS_TYPE_NAME) != 0)
|
||||
goto err;
|
||||
|
||||
if (pnp->get_attr_string(parent, IDE_CONTROLLER_CONTROLLER_NAME_ITEM,
|
||||
&controller_name, true) != B_OK) {
|
||||
pnp->get_attr_string(parent, PNP_DRIVER_DRIVER, &str, false);
|
||||
SHOW_ERROR( 0, "Ignored controller managed by %s - controller name missing", str);
|
||||
goto err;
|
||||
}
|
||||
|
||||
channel_id = pnp->create_id(IDE_CHANNEL_ID_GENERATOR);
|
||||
|
||||
if (channel_id < 0) {
|
||||
SHOW_ERROR(0, "Cannot register IDE controller %s - out of IDs", controller_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
{
|
||||
pnp_node_attr attrs[] =
|
||||
{
|
||||
{ PNP_DRIVER_DRIVER, B_STRING_TYPE, { string: IDE_SIM_MODULE_NAME }},
|
||||
{ PNP_DRIVER_TYPE, B_STRING_TYPE, { string: SCSI_SIM_TYPE_NAME }},
|
||||
{ PNP_DRIVER_FIXED_CONSUMER, B_STRING_TYPE, { string: SCSI_FOR_SIM_MODULE_NAME }},
|
||||
{ SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE,
|
||||
{ string: controller_name }},
|
||||
// maximum number of blocks per transmission:
|
||||
// - ATAPI uses packets, i.e. normal SCSI limits apply
|
||||
// but I'm not sure about controller restrictions
|
||||
// - ATA allows up to 256 blocks
|
||||
// - some broken disk's firmware (read: IBM DTTA drives)
|
||||
// don't like 256 blocks in command queuing mode
|
||||
// -> use 255 blocks as a least common nominator
|
||||
// (this is still 127.5K for HDs and 510K for CDs,
|
||||
// which should be sufficient)
|
||||
// Note: to fix specific drive bugs, use ide_sim_get_restrictions()
|
||||
// in ide_sim.c!
|
||||
{ BLKDEV_MAX_BLOCKS_ITEM, B_UINT32_TYPE, { ui32: 255 }},
|
||||
{ IDE_CHANNEL_ID_ITEM, B_UINT32_TYPE, { ui32: channel_id }},
|
||||
{ PNP_MANAGER_ID_GENERATOR, B_STRING_TYPE, { string: IDE_CHANNEL_ID_GENERATOR }},
|
||||
{ PNP_MANAGER_AUTO_ID, B_UINT32_TYPE, { ui32: channel_id }},
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
pnp_node_handle node;
|
||||
status_t res;
|
||||
|
||||
res = pnp->register_device(parent, attrs, NULL, &node);
|
||||
|
||||
free(str);
|
||||
free(controller_name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
err:
|
||||
free(str);
|
||||
free(controller_name);
|
||||
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ide_for_controller_interface ide_for_controller_module = {
|
||||
{
|
||||
{
|
||||
IDE_FOR_CONTROLLER_MODULE_NAME,
|
||||
0,
|
||||
&std_ops
|
||||
},
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
ide_channel_added,
|
||||
NULL
|
||||
},
|
||||
|
||||
ide_irq_handler
|
||||
};
|
318
src/add-ons/kernel/bus_managers/ide/devices.c
Normal file
318
src/add-ons/kernel/bus_managers/ide/devices.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Device manager
|
||||
|
||||
As the IDE bus manager is an SCSI to IDE translater, it
|
||||
has to know a bit more about connected devices then a standard
|
||||
SIM. This file contains device detection and classification.
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
#include "ide_cmds.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <ByteOrder.h>
|
||||
|
||||
|
||||
/** cleanup links devices on one bus when <device> is deleted */
|
||||
|
||||
static void
|
||||
cleanup_device_links(ide_device_info *device)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
bus->devices[device->is_device1] = NULL;
|
||||
|
||||
if (device->other_device) {
|
||||
if (device->other_device != device) {
|
||||
device->other_device->other_device = device->other_device;
|
||||
bus->first_device = device->other_device;
|
||||
} else
|
||||
bus->first_device = NULL;
|
||||
}
|
||||
|
||||
device->other_device = NULL;
|
||||
}
|
||||
|
||||
|
||||
/** destroy device info */
|
||||
|
||||
static void
|
||||
destroy_device(ide_device_info *device)
|
||||
{
|
||||
SHOW_FLOW( 4, "%p", device );
|
||||
|
||||
// paranoia
|
||||
device->exec_io = NULL;
|
||||
cancel_timer(&device->reconnect_timer.te);
|
||||
|
||||
scsi->free_dpc(device->reconnect_timeout_dpc);
|
||||
|
||||
cleanup_device_links(device);
|
||||
|
||||
destroy_qreq_array(device);
|
||||
|
||||
uninit_synced_pc(&device->reconnect_timeout_synced_pc);
|
||||
|
||||
free(device);
|
||||
}
|
||||
|
||||
|
||||
/** setup links between the devices on one bus */
|
||||
|
||||
static void
|
||||
setup_device_links(ide_bus_info *bus, ide_device_info *device)
|
||||
{
|
||||
SHOW_FLOW0( 4, "" );
|
||||
|
||||
device->bus = bus;
|
||||
bus->devices[device->is_device1] = device;
|
||||
|
||||
device->other_device = device;
|
||||
|
||||
if (device->is_device1) {
|
||||
if (bus->devices[0]) {
|
||||
device->other_device = bus->devices[0];
|
||||
bus->devices[0]->other_device = device;
|
||||
}
|
||||
} else {
|
||||
if (bus->devices[1]) {
|
||||
device->other_device = bus->devices[1];
|
||||
bus->devices[1]->other_device = device;
|
||||
}
|
||||
}
|
||||
|
||||
if (bus->first_device == NULL)
|
||||
bus->first_device = device;
|
||||
}
|
||||
|
||||
|
||||
/** create device info */
|
||||
|
||||
static
|
||||
ide_device_info *create_device(ide_bus_info *bus, bool is_device1)
|
||||
{
|
||||
ide_device_info *device;
|
||||
|
||||
SHOW_FLOW(4, "is_device1=%d", (int)is_device1);
|
||||
|
||||
device = (ide_device_info *)malloc(sizeof(*device));
|
||||
if (device == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(device, 0, sizeof(*device));
|
||||
|
||||
device->is_device1 = is_device1;
|
||||
device->target_id = is_device1;
|
||||
|
||||
setup_device_links(bus, device);
|
||||
|
||||
device->DMA_failures = 0;
|
||||
device->CQ_failures = 0;
|
||||
device->num_failed_send = 0;
|
||||
|
||||
device->combined_sense = 0;
|
||||
|
||||
device->num_running_reqs = 0;
|
||||
|
||||
device->reconnect_timer.device = device;
|
||||
|
||||
init_synced_pc(&device->reconnect_timeout_synced_pc,
|
||||
reconnect_timeout_worker);
|
||||
|
||||
if (scsi->alloc_dpc(&device->reconnect_timeout_dpc) != B_OK)
|
||||
goto err;
|
||||
|
||||
device->total_sectors = 0;
|
||||
return device;
|
||||
|
||||
err:
|
||||
destroy_device(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if B_HOST_IS_LENDIAN
|
||||
|
||||
#define B_BENDIAN_TO_HOST_MULTI(v, n) do { \
|
||||
size_t __swap16_multi_n = (n); \
|
||||
uint16 *__swap16_multi_v = (v); \
|
||||
\
|
||||
while( __swap16_multi_n ) { \
|
||||
*__swap16_multi_v = B_SWAP_INT16(*__swap16_multi_v); \
|
||||
__swap16_multi_v++; \
|
||||
__swap16_multi_n--; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define B_BENDIAN_TO_HOST_MULTI(v, n)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** prepare infoblock for further use, i.e. fix endianess */
|
||||
|
||||
static void
|
||||
prep_infoblock(ide_device_info *device)
|
||||
{
|
||||
ide_device_infoblock *infoblock = &device->infoblock;
|
||||
|
||||
B_BENDIAN_TO_HOST_MULTI((uint16 *)infoblock->serial_number,
|
||||
sizeof(infoblock->serial_number) / 2);
|
||||
|
||||
B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->firmware_version,
|
||||
sizeof(infoblock->firmware_version) / 2);
|
||||
|
||||
B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->model_number,
|
||||
sizeof(infoblock->model_number) / 2);
|
||||
|
||||
infoblock->LBA_total_sectors = B_LENDIAN_TO_HOST_INT32(infoblock->LBA_total_sectors);
|
||||
infoblock->LBA48_total_sectors = B_LENDIAN_TO_HOST_INT64(infoblock->LBA48_total_sectors);
|
||||
}
|
||||
|
||||
|
||||
/** read info block of ATA or ATAPI device */
|
||||
|
||||
static bool
|
||||
scan_device_int(ide_device_info *device, bool atapi)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
int status;
|
||||
|
||||
FAST_LOG1(bus->log, ev_ide_scan_device_int, (uint32)atapi);
|
||||
|
||||
device->tf_param_mask = 0;
|
||||
device->tf.write.command = atapi ? IDE_CMD_IDENTIFY_PACKET_DEVICE
|
||||
: IDE_CMD_IDENTIFY_DEVICE;
|
||||
|
||||
// initialize device selection flags,
|
||||
// this is the only place where this bit gets initialized in the task file
|
||||
if (bus->controller->read_command_block_regs(bus->channel, &device->tf,
|
||||
ide_mask_device_head) != B_OK)
|
||||
return false;
|
||||
|
||||
device->tf.lba.device = device->is_device1;
|
||||
|
||||
if (!send_command(device, NULL, atapi ? false : true, 20, ide_state_sync_waiting)) {
|
||||
FAST_LOG0(bus->log, ev_ide_scan_device_int_cant_send);
|
||||
return false;
|
||||
}
|
||||
|
||||
// do a short wait first - if there's no device at all we could wait forever
|
||||
if (acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 1000/*100000*/) == B_TIMED_OUT) {
|
||||
bool cont;
|
||||
|
||||
SHOW_FLOW0( 3, "no fast response to inquiry" );
|
||||
|
||||
// check the busy flag - if it's still set, there's probably no device
|
||||
IDE_LOCK(bus);
|
||||
|
||||
status = bus->controller->get_altstatus(bus->channel);
|
||||
SHOW_FLOW(3, "status=%x", (int)status);
|
||||
cont = (status & ide_status_bsy) == ide_status_bsy;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
if (!cont) {
|
||||
SHOW_FLOW0( 3, "no busy bit set after 100ms - probably noone there" );
|
||||
// no reaction -> abort waiting
|
||||
cancel_irq_timeout(bus);
|
||||
|
||||
// timeout or irq may have been fired, reset semaphore just is case
|
||||
acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 0);
|
||||
|
||||
FAST_LOG0(bus->log, ev_ide_scan_device_int_keeps_busy);
|
||||
return false;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "busy bit set, give device more time" );
|
||||
// there is something, so wait for it
|
||||
acquire_sem(bus->sync_wait_sem);
|
||||
}
|
||||
|
||||
// cancel the timeout manually; usuall this is done by wait_for_sync(), but
|
||||
// we've used the wait semaphore directly
|
||||
cancel_irq_timeout(bus);
|
||||
|
||||
if (bus->sync_wait_timeout)
|
||||
return false;
|
||||
|
||||
ide_wait(device, ide_status_drq, ide_status_bsy, true, 1000);
|
||||
|
||||
status = bus->controller->get_altstatus(bus->channel);
|
||||
|
||||
if ((status & ide_status_err) != 0) {
|
||||
// if there's no device, all bits including the error bit are set
|
||||
SHOW_FLOW( 3, "err set - no device or wrong type (status: %x)", status );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the infoblock
|
||||
bus->controller->read_pio(bus->channel, (uint16 *)&device->infoblock,
|
||||
sizeof(device->infoblock) / sizeof(uint16), false);
|
||||
|
||||
if (!wait_for_drqdown(device))
|
||||
return false;
|
||||
|
||||
SHOW_FLOW0( 3, "device found" );
|
||||
|
||||
FAST_LOG0(bus->log, ev_ide_scan_device_int_found);
|
||||
|
||||
prep_infoblock(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** scan one device */
|
||||
|
||||
void
|
||||
scan_device_worker(ide_bus_info *bus, void *arg)
|
||||
{
|
||||
int is_device1 = (int)arg;
|
||||
ide_device_info *device;
|
||||
|
||||
SHOW_FLOW(4, "is_device1: %d", is_device1);
|
||||
|
||||
// forget everything we know about the device;
|
||||
// don't care about peripheral drivers using this device
|
||||
// as the device info is only used by us and not published
|
||||
// directly or indirectly to the SCSI bus manager
|
||||
if (bus->devices[is_device1])
|
||||
destroy_device(bus->devices[is_device1]);
|
||||
|
||||
device = create_device(bus, is_device1);
|
||||
|
||||
// reset status so we can see what goes wrong during detection
|
||||
device->subsys_status = SCSI_REQ_CMP;
|
||||
|
||||
if (scan_device_int(device, false)) {
|
||||
if (!prep_ata(device))
|
||||
goto err;
|
||||
} else if (device->subsys_status != SCSI_TID_INVALID
|
||||
&& scan_device_int(device, true)) {
|
||||
// only try ATAPI if there is at least some device
|
||||
// (see send_command - this error code must be unique!)
|
||||
if (!prep_atapi(device))
|
||||
goto err;
|
||||
} else
|
||||
goto err;
|
||||
|
||||
bus->state = ide_state_idle;
|
||||
release_sem(bus->scan_device_sem);
|
||||
return;
|
||||
|
||||
err:
|
||||
destroy_device(device);
|
||||
|
||||
bus->state = ide_state_idle;
|
||||
release_sem(bus->scan_device_sem);
|
||||
}
|
143
src/add-ons/kernel/bus_managers/ide/dma.c
Normal file
143
src/add-ons/kernel/bus_managers/ide/dma.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
DMA helper functions
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
|
||||
|
||||
#define CHECK_DEV_DMA_MODE(infoblock, elem, mode, this_mode, num_modes ) \
|
||||
if( infoblock->elem ) { \
|
||||
mode = this_mode; \
|
||||
++num_modes; \
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_device_dma_mode(ide_device_info *device)
|
||||
{
|
||||
ide_device_infoblock *infoblock = &device->infoblock;
|
||||
|
||||
int num_modes, mode;
|
||||
|
||||
mode = 0;
|
||||
num_modes = 0;
|
||||
|
||||
if (!infoblock->DMA_supported)
|
||||
return -1;
|
||||
|
||||
CHECK_DEV_DMA_MODE(infoblock, MDMA0_selected, mode, 0, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, MDMA1_selected, mode, 1, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, MDMA2_selected, mode, 2, num_modes);
|
||||
|
||||
if (infoblock->_88_valid) {
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA0_selected, mode, 0x10, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA1_selected, mode, 0x11, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA2_selected, mode, 0x12, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA3_selected, mode, 0x13, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA4_selected, mode, 0x14, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA5_selected, mode, 0x15, num_modes);
|
||||
CHECK_DEV_DMA_MODE(infoblock, UDMA6_selected, mode, 0x16, num_modes);
|
||||
}
|
||||
|
||||
if (num_modes != 1)
|
||||
return -1;
|
||||
|
||||
SHOW_FLOW(3, "%x", mode);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
configure_dma(ide_device_info *device)
|
||||
{
|
||||
device->DMA_enabled = device->DMA_supported = device->bus->can_DMA
|
||||
&& get_device_dma_mode(device) != -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** abort DMA transmission
|
||||
* must be called _before_ start_dma_wait
|
||||
*/
|
||||
|
||||
void
|
||||
abort_dma(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
SHOW_FLOW0(0, "");
|
||||
|
||||
bus->controller->finish_dma(bus->channel);
|
||||
}
|
||||
|
||||
|
||||
/** prepare DMA transmission
|
||||
* on return, DMA engine waits for device to transmit data
|
||||
* warning: doesn't set sense data on error
|
||||
*/
|
||||
|
||||
bool
|
||||
prepare_dma(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
scsi_ccb *request = qrequest->request;
|
||||
status_t res;
|
||||
|
||||
res = bus->controller->prepare_dma(bus->channel, request->sg_list,
|
||||
request->sg_cnt, qrequest->is_write);
|
||||
|
||||
if (res != B_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** start waiting for DMA to be finished */
|
||||
|
||||
void
|
||||
start_dma_wait(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
bus->controller->start_dma(bus->channel);
|
||||
|
||||
start_waiting(bus, qrequest->request->timeout > 0 ?
|
||||
qrequest->request->timeout : IDE_STD_TIMEOUT, ide_state_async_waiting);
|
||||
}
|
||||
|
||||
|
||||
/** start waiting for DMA to be finished with bus lock not hold */
|
||||
|
||||
void
|
||||
start_dma_wait_no_lock(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
IDE_LOCK(bus);
|
||||
start_dma_wait(device, qrequest);
|
||||
}
|
||||
|
||||
|
||||
/** finish dma transmission after device has fired IRQ */
|
||||
|
||||
bool
|
||||
finish_dma(ide_device_info *device)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
status_t dma_res;
|
||||
|
||||
dma_res = bus->controller->finish_dma(bus->channel);
|
||||
|
||||
return dma_res == B_OK || dma_res == B_DEV_DATA_OVERRUN;
|
||||
}
|
||||
|
117
src/add-ons/kernel/bus_managers/ide/emulation.c
Normal file
117
src/add-ons/kernel/bus_managers/ide/emulation.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
General SCSI emulation routines
|
||||
*/
|
||||
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
#include "KernelExport_ext.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** emulate REQUEST SENSE */
|
||||
|
||||
void
|
||||
ide_request_sense(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
scsi_cmd_request_sense *cmd = (scsi_cmd_request_sense *)request->cdb;
|
||||
scsi_sense sense;
|
||||
uint32 transfer_size;
|
||||
|
||||
// cannot use finish_checksense here, as data is not copied into autosense buffer
|
||||
// but into normal data buffer, SCSI result is GOOD and CAM status is REQ_CMP
|
||||
|
||||
if (device->combined_sense)
|
||||
create_sense(device, &sense);
|
||||
else
|
||||
memset(&sense, 0, sizeof(sense));
|
||||
|
||||
copy_sg_data(request, 0, cmd->alloc_length, &sense, sizeof(sense), false);
|
||||
|
||||
// reset sense information on read
|
||||
device->combined_sense = 0;
|
||||
|
||||
transfer_size = min(sizeof(sense), cmd->alloc_length);
|
||||
transfer_size = min(transfer_size, request->data_len);
|
||||
|
||||
request->data_resid = request->data_len - transfer_size;
|
||||
|
||||
// normally, all flags are set to "success", but for Request Sense
|
||||
// this would have overwritten the sense we want to read
|
||||
device->subsys_status = SCSI_REQ_CMP;
|
||||
request->device_status = SCSI_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
/** copy data between request data and buffer
|
||||
* request - request to copy data from/to
|
||||
* offset - offset of data in request
|
||||
* allocation_length- limit of request's data buffer according to CDB
|
||||
* buffer - data to copy data from/to
|
||||
* size - number of bytes to copy
|
||||
* to_buffer - true: copy from request to buffer
|
||||
* false: copy from buffer to request
|
||||
* return: true, if data of request was large enough
|
||||
*/
|
||||
|
||||
bool
|
||||
copy_sg_data(scsi_ccb *request, uint offset, uint allocation_length,
|
||||
void *buffer, int size, bool to_buffer)
|
||||
{
|
||||
const physical_entry *sg_list = request->sg_list;
|
||||
int sg_cnt = request->sg_cnt;
|
||||
int req_size;
|
||||
|
||||
SHOW_FLOW(3, "offset=%u, req_size_limit=%d, size=%d, sg_list=%p, sg_cnt=%d, %s buffer",
|
||||
offset, allocation_length, size, sg_list, sg_cnt, to_buffer ? "to" : "from");
|
||||
|
||||
// skip unused S/G entries
|
||||
while (sg_cnt > 0 && offset >= sg_list->size) {
|
||||
offset -= sg_list->size;
|
||||
++sg_list;
|
||||
--sg_cnt;
|
||||
}
|
||||
|
||||
if (sg_cnt == 0)
|
||||
return 0;
|
||||
|
||||
// remaining bytes we are allowed to copy from/to request
|
||||
req_size = min(allocation_length, request->data_len) - offset;
|
||||
|
||||
// copy one S/G entry at a time
|
||||
for (; size > 0 && req_size > 0 && sg_cnt > 0; ++sg_list, --sg_cnt) {
|
||||
size_t bytes;
|
||||
void *virt_addr;
|
||||
|
||||
bytes = min(size, req_size);
|
||||
bytes = min(bytes, sg_list->size);
|
||||
|
||||
if (map_mainmemory((addr_t)sg_list->address, &virt_addr) != B_OK)
|
||||
return false;
|
||||
|
||||
SHOW_FLOW(4, "buffer=%p, virt_addr=%p, bytes=%d, to_buffer=%d",
|
||||
buffer, (void *)((addr_t)virt_addr + offset), (int)bytes, to_buffer);
|
||||
|
||||
if (to_buffer)
|
||||
memcpy(buffer, (void *)((addr_t)virt_addr + offset), bytes);
|
||||
else
|
||||
memcpy((void *)((addr_t)virt_addr + offset), buffer, bytes);
|
||||
|
||||
unmap_mainmemory(virt_addr);
|
||||
|
||||
(char *)buffer += bytes;
|
||||
size -= bytes;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return size == 0;
|
||||
}
|
28
src/add-ons/kernel/bus_managers/ide/ide.c
Normal file
28
src/add-ons/kernel/bus_managers/ide/ide.c
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Main file
|
||||
|
||||
Contains interface used by IDE controller driver.
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
|
||||
|
||||
device_manager_info *pnp;
|
||||
|
||||
|
||||
#if !_BUILDING_kernel && !BOOT
|
||||
_EXPORT
|
||||
module_info *modules[] = {
|
||||
(module_info *)&ide_for_controller_module,
|
||||
(module_info *)&ide_sim_module,
|
||||
NULL
|
||||
};
|
||||
#endif
|
64
src/add-ons/kernel/bus_managers/ide/ide_cmds.h
Normal file
64
src/add-ons/kernel/bus_managers/ide/ide_cmds.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
OP-Codes of IDE commands
|
||||
*/
|
||||
|
||||
#ifndef __IDE_CMDS_H__
|
||||
#define __IDE_CMDS_H__
|
||||
|
||||
|
||||
#define IDE_CMD_WRITE_DMA 0xca
|
||||
#define IDE_CMD_WRITE_DMA_QUEUED 0xcc
|
||||
#define IDE_CMD_WRITE_MULTIPLE 0xc5
|
||||
#define IDE_CMD_WRITE_SECTORS 0x30
|
||||
|
||||
#define IDE_CMD_READ_DMA 0xc8
|
||||
#define IDE_CMD_READ_DMA_QUEUED 0xc7
|
||||
#define IDE_CMD_READ_MULTIPLE 0xc4
|
||||
#define IDE_CMD_READ_SECTORS 0x20
|
||||
|
||||
#define IDE_CMD_WRITE_DMA_EXT 0x35
|
||||
#define IDE_CMD_WRITE_DMA_QUEUED_EXT 0x36
|
||||
#define IDE_CMD_WRITE_MULTIPLE_EXT 0x39
|
||||
#define IDE_CMD_WRITE_SECTORS_EXT 0x34
|
||||
|
||||
#define IDE_CMD_READ_DMA_EXT 0x25
|
||||
#define IDE_CMD_READ_DMA_QUEUED_EXT 0x26
|
||||
#define IDE_CMD_READ_MULTIPLE_EXT 0x29
|
||||
#define IDE_CMD_READ_SECTORS_EXT 0x24
|
||||
|
||||
#define IDE_CMD_PACKET 0xa0
|
||||
#define IDE_CMD_DEVICE_RESET 0x08
|
||||
|
||||
#define IDE_CMD_SERVICE 0xa2
|
||||
#define IDE_CMD_NOP 0
|
||||
|
||||
#define IDE_CMD_NOP_NOP 0
|
||||
#define IDE_CMD_NOP_NOP_AUTOPOLL 1
|
||||
|
||||
|
||||
#define IDE_CMD_GET_MEDIA_STATUS 0xda
|
||||
|
||||
#define IDE_CMD_FLUSH_CACHE 0xe7
|
||||
#define IDE_CMD_FLUSH_CACHE_EXT 0xea
|
||||
|
||||
#define IDE_CMD_MEDIA_EJECT 0xed
|
||||
|
||||
#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xa1
|
||||
#define IDE_CMD_IDENTIFY_DEVICE 0xec
|
||||
|
||||
#define IDE_CMD_SET_FEATURES 0xef
|
||||
#define IDE_CMD_SET_FEATURES_ENABLE_REL_INT 0x5d
|
||||
#define IDE_CMD_SET_FEATURES_ENABLE_SERV_INT 0x5e
|
||||
#define IDE_CMD_SET_FEATURES_DISABLE_REL_INT 0xdd
|
||||
#define IDE_CMD_SET_FEATURES_DISABLE_SERV_INT 0xde
|
||||
#define IDE_CMD_SET_FEATURES_ENABLE_MSN 0x95
|
||||
|
||||
|
||||
#endif
|
185
src/add-ons/kernel/bus_managers/ide/ide_device_infoblock.h
Normal file
185
src/add-ons/kernel/bus_managers/ide/ide_device_infoblock.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Definition of response to IDE_CMD_IDENTIFY_DEVICE or
|
||||
IDE_CMD_IDENTIFY_PACKET_DEVICE
|
||||
|
||||
When a new entry is inserted, add its offset in hex
|
||||
and its index in decimal as a remark. Without that, you
|
||||
have a rough time when you messed up the offsets.
|
||||
*/
|
||||
|
||||
#ifndef __IDE_DEVICE_INFOBLOCK_H__
|
||||
#define __IDE_DEVICE_INFOBLOCK_H__
|
||||
|
||||
#include <lendian_bitfield.h>
|
||||
|
||||
// must be 512 bytes!!!
|
||||
typedef struct tagdevice_infoblock {
|
||||
union { // 0 general configuration
|
||||
struct {
|
||||
LBITFIELD8 (
|
||||
_0_res1 : 1,
|
||||
_0_ret1 : 1,
|
||||
response_incomplete : 1,
|
||||
_0_ret2 : 3,
|
||||
removable_controller_or_media : 1,
|
||||
removable_media : 1,
|
||||
_0_ret3 : 7,
|
||||
ATA : 1 // 0 - is ATA!
|
||||
);
|
||||
} ata;
|
||||
struct {
|
||||
LBITFIELD8 (
|
||||
packet_size : 2, // 0 - 12 bytes, 1 - 16 bytes
|
||||
response_incomplete : 1,
|
||||
_0_res2 : 2,
|
||||
drq_speed : 2, // 0 - 3ms, 1 - IRQ, 2 - 50µs
|
||||
removable_media : 1,
|
||||
type : 5,
|
||||
_0_res13 : 1,
|
||||
ATAPI : 2 // 2 - is ATAPI
|
||||
);
|
||||
} atapi;
|
||||
} _0;
|
||||
uint16 cylinders; // 2
|
||||
uint16 dummy1; // 4
|
||||
uint16 heads; // 6
|
||||
uint16 dummy2[2]; // 8
|
||||
uint16 sectors; // 0c
|
||||
uint16 dummy3[3]; // 0e
|
||||
char serial_number[20]; // 14
|
||||
uint16 dummy4[3]; // 28
|
||||
char firmware_version[8]; // 2e
|
||||
char model_number[40]; // 36
|
||||
uint16 dummy5[2]; // 5e
|
||||
LBITFIELD5 ( // 62 (49) capabilities
|
||||
_49_ret1 : 8,
|
||||
DMA_supported : 1,
|
||||
LBA_supported : 1,
|
||||
IORDY_can_disable : 1,
|
||||
IORDY_supported : 1
|
||||
);
|
||||
|
||||
uint16 dummy6[1]; // 64
|
||||
LBITFIELD2 ( // 66 (51) obsolete: PIO modes?
|
||||
_51_obs1 : 8,
|
||||
PIO_mode : 8
|
||||
);
|
||||
uint16 dummy7[1]; // 68
|
||||
|
||||
|
||||
LBITFIELD3 ( // 6a (53) validity
|
||||
_54_58_valid : 1,
|
||||
_64_70_valid : 1,
|
||||
_88_valid : 1
|
||||
);
|
||||
uint16 current_cylinders; // 6c (54)
|
||||
uint16 current_heads; // 6e
|
||||
uint16 current_sectors; // 70
|
||||
|
||||
uint16 capacity_low; // 72 (57) ALIGNMENT SPLIT - don't merge
|
||||
uint16 capacity_high;
|
||||
|
||||
uint16 dummy8[1];
|
||||
|
||||
uint32 LBA_total_sectors; // 78 (60)
|
||||
uint16 dummy9[1]; // 7c
|
||||
|
||||
LBITFIELD7 ( // 7e (63) MDMA modes
|
||||
MDMA0_supported : 1,
|
||||
MDMA1_supported : 1,
|
||||
MDMA2_supported : 1,
|
||||
_63_res1 : 5,
|
||||
MDMA0_selected : 1,
|
||||
MDMA1_selected : 1,
|
||||
MDMA2_selected : 1
|
||||
);
|
||||
uint16 dummy10[11]; // 80
|
||||
|
||||
LBITFIELD2 ( // 96 (75)
|
||||
queue_depth : 5,
|
||||
_75_res1 : 9
|
||||
);
|
||||
uint16 dummy11[6]; // 98
|
||||
|
||||
LBITFIELD16 ( // a4 (82) supported_command_set
|
||||
SMART_supported : 1,
|
||||
security_mode_supported : 1,
|
||||
removable_media_supported : 1,
|
||||
PM_supported : 1,
|
||||
_81_fixed : 1, // must be 0
|
||||
write_cache_supported : 1,
|
||||
look_ahead_supported : 1,
|
||||
RELEASE_irq_supported : 1,
|
||||
|
||||
SERVICE_irq_supported : 1,
|
||||
DEVICE_RESET_supported : 1,
|
||||
HPA_supported : 1,
|
||||
_81_obs1 : 1,
|
||||
WRITE_BUFFER_supported : 1,
|
||||
READ_BUFFER_supported : 1,
|
||||
NOP_supported : 1,
|
||||
_81_obs2 : 1
|
||||
);
|
||||
LBITFIELD15 ( // a6 (83) supported_command_sets
|
||||
DOWNLOAD_MICROCODE_supported : 1,
|
||||
DMA_QUEUED_supported : 1,
|
||||
CFA_supported : 1,
|
||||
APM_supported : 1,
|
||||
RMSN_supported : 1,
|
||||
power_up_in_stand_by_supported : 1,
|
||||
SET_FEATURES_on_power_up_required : 1,
|
||||
reserved_boot_area_supported : 1,
|
||||
SET_MAX_security_supported : 1,
|
||||
auto_acustic_managemene_supported : 1,
|
||||
_48_bit_addresses_supported : 1,
|
||||
device_conf_overlay_supported : 1,
|
||||
FLUSH_CACHE_supported : 1,
|
||||
FLUSH_CACHE_EXT_supported : 1,
|
||||
_83_fixed : 2 // must be 1
|
||||
);
|
||||
|
||||
uint16 dummy12[4]; // a8 (84)
|
||||
LBITFIELD15 ( // b0 (88) UDMA modes
|
||||
UDMA0_supported : 1,
|
||||
UDMA1_supported : 1,
|
||||
UDMA2_supported : 1,
|
||||
UDMA3_supported : 1,
|
||||
UDMA4_supported : 1,
|
||||
UDMA5_supported : 1,
|
||||
UDMA6_supported : 1, // !guessed
|
||||
_88_res1 : 1,
|
||||
UDMA0_selected : 1,
|
||||
UDMA1_selected : 1,
|
||||
UDMA2_selected : 1,
|
||||
UDMA3_selected : 1,
|
||||
UDMA4_selected : 1,
|
||||
UDMA5_selected : 1,
|
||||
UDMA6_selected : 1
|
||||
);
|
||||
|
||||
uint16 dummy89[11]; // b2 (89)
|
||||
uint64 LBA48_total_sectors; // c8 (100)
|
||||
uint16 dummy102[22]; // cc (104)
|
||||
|
||||
LBITFIELD2 ( // fc (126)
|
||||
last_lun : 2,
|
||||
_126_res2 : 14
|
||||
);
|
||||
LBITFIELD4 ( // fe (127) RMSN support
|
||||
_127_RMSN_support : 2,// 0 = not supported, 1 = supported, 3, 4 = reserved
|
||||
_127_res2 : 6,
|
||||
device_write_protect: 2,
|
||||
_127_res9 : 6
|
||||
);
|
||||
uint16 dummy14[128]; // 100 (128)
|
||||
} ide_device_infoblock;
|
||||
|
||||
|
||||
#endif
|
428
src/add-ons/kernel/bus_managers/ide/ide_internal.h
Normal file
428
src/add-ons/kernel/bus_managers/ide/ide_internal.h
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Internal structures
|
||||
*/
|
||||
|
||||
#ifndef __IDE_INTERNAL_H__
|
||||
#define __IDE_INTERNAL_H__
|
||||
|
||||
#include <bus/IDE.h>
|
||||
#include <bus/SCSI.h>
|
||||
#include "ide_device_infoblock.h"
|
||||
#include <device_manager.h>
|
||||
#include <fast_log.h>
|
||||
|
||||
#define debug_level_error 4
|
||||
#define debug_level_info 4
|
||||
#define debug_level_flow 4
|
||||
|
||||
#define DEBUG_MSG_PREFIX "IDE -- "
|
||||
|
||||
#include "wrapper.h"
|
||||
|
||||
|
||||
//#define USE_FAST_LOG
|
||||
|
||||
#ifdef USE_FAST_LOG
|
||||
#define FAST_LOG0( handle, event ) fast_log->log_0( handle, event )
|
||||
#define FAST_LOG1( handle, event, param ) fast_log->log_1( handle, event, param )
|
||||
#define FAST_LOG2( handle, event, param1, param2 ) fast_log->log_2( handle, event, param1, param2 )
|
||||
#define FAST_LOG3( handle, event, param1, param2, param3 ) fast_log->log_3( handle, event, param1, param2, param3 )
|
||||
#define FAST_LOGN( handle, event, num_params... ) fast_log->log_n( handle, event, num_params )
|
||||
#else
|
||||
#define FAST_LOG0( handle, event )
|
||||
#define FAST_LOG1( handle, event, param )
|
||||
#define FAST_LOG2( handle, event, param1, param2 )
|
||||
#define FAST_LOG3( handle, event, param1, param2, param3 )
|
||||
#define FAST_LOGN( handle, event, num_params... )
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define IDE_STD_TIMEOUT 10
|
||||
#define IDE_RELEASE_TIMEOUT 10000000
|
||||
|
||||
// number of timeouts before we disable DMA automatically
|
||||
#define MAX_DMA_FAILURES 3
|
||||
|
||||
// name of pnp generator of channel ids
|
||||
#define IDE_CHANNEL_ID_GENERATOR "ide/channel_id"
|
||||
// node item containing channel id (uint32)
|
||||
#define IDE_CHANNEL_ID_ITEM "ide/channel_id"
|
||||
|
||||
extern device_manager_info *pnp;
|
||||
extern fast_log_info *fast_log;
|
||||
|
||||
|
||||
typedef struct ide_bus_info ide_bus_info;
|
||||
|
||||
typedef void (*ide_synced_pc_func)(ide_bus_info *bus, void *arg);
|
||||
|
||||
typedef struct ide_synced_pc {
|
||||
struct ide_synced_pc *next;
|
||||
ide_synced_pc_func func;
|
||||
void *arg;
|
||||
bool registered;
|
||||
} ide_synced_pc;
|
||||
|
||||
// structure for device time-outs
|
||||
typedef struct ide_device_timer_info {
|
||||
timer te;
|
||||
struct ide_device_info *device;
|
||||
} ide_device_timer_info;
|
||||
|
||||
// structure for bus time-outs
|
||||
typedef struct ide_bus_timer_info {
|
||||
timer te;
|
||||
struct ide_bus_info *bus;
|
||||
} ide_bus_timer_info;
|
||||
|
||||
|
||||
typedef struct ide_device_info {
|
||||
struct ide_bus_info *bus;
|
||||
|
||||
uint8 use_LBA : 1; // true for LBA, false for CHS
|
||||
uint8 use_48bits : 1; // true for LBA48
|
||||
uint8 is_atapi : 1; // true for ATAPI, false for ATA
|
||||
uint8 CQ_supported : 1; // Command Queuing supported
|
||||
uint8 CQ_enabled : 1; // Command Queuing enabled
|
||||
uint8 DMA_supported : 1; // DMA supported
|
||||
uint8 DMA_enabled : 1; // DMA enabled
|
||||
uint8 is_device1 : 1; // true for slave, false for master
|
||||
|
||||
uint8 queue_depth; // maximum Command Queueing depth
|
||||
|
||||
uint8 last_lun; // last LUN
|
||||
|
||||
uint8 DMA_failures; // DMA failures in a row
|
||||
uint8 CQ_failures; // Command Queuing failures during _last_ command
|
||||
uint8 num_failed_send; // number of consequetive send problems
|
||||
|
||||
// next two error codes are copied to request on finish_request & co.
|
||||
uint8 subsys_status; // subsystem status of current request
|
||||
uint32 new_combined_sense; // emulated sense of current request
|
||||
|
||||
// pending error codes
|
||||
uint32 combined_sense; // emulated sense of device
|
||||
|
||||
struct ide_qrequest *qreq_array; // array of ide requests
|
||||
struct ide_qrequest *free_qrequests; // free list
|
||||
int num_running_reqs; // number of running requests
|
||||
|
||||
struct ide_device_info *other_device; // other device on same bus
|
||||
|
||||
// entry for scsi's exec_io request
|
||||
void (*exec_io)( struct ide_device_info *device, struct ide_qrequest *qrequest );
|
||||
|
||||
int target_id; // target id (currently, same as is_device1)
|
||||
|
||||
ide_reg_mask tf_param_mask; // flag of valid bytes in task file
|
||||
ide_task_file tf; // task file
|
||||
|
||||
// ata from here on
|
||||
uint64 total_sectors; // size in sectors
|
||||
|
||||
// atapi from here on
|
||||
uint8 packet[12]; // atapi command packet
|
||||
|
||||
struct {
|
||||
uint8 packet_irq : 1; // true, if command packet irq required
|
||||
bigtime_t packet_irq_timeout; // timeout for it
|
||||
} atapi;
|
||||
|
||||
uint8 device_type; // atapi device type
|
||||
|
||||
bool reconnect_timer_installed; // true, if reconnect timer is running
|
||||
ide_device_timer_info reconnect_timer; // reconnect timeout
|
||||
scsi_dpc_cookie reconnect_timeout_dpc; // dpc fired by timeout
|
||||
ide_synced_pc reconnect_timeout_synced_pc; // spc fired by dpc
|
||||
|
||||
// pio from here on
|
||||
int left_sg_elem; // remaining sg elements
|
||||
const physical_entry *cur_sg_elem; // active sg element
|
||||
int cur_sg_ofs; // offset in active sg element
|
||||
|
||||
int left_blocks; // remaining blocks
|
||||
|
||||
bool has_odd_byte; // remaining odd byte
|
||||
int odd_byte; // content off odd byte
|
||||
|
||||
ide_device_infoblock infoblock; // infoblock of device
|
||||
} ide_device_info;
|
||||
|
||||
|
||||
/*// state as stored in sim_state of scsi_ccb
|
||||
typedef enum {
|
||||
ide_request_normal = 0, // this must be zero as this is initial value
|
||||
ide_request_start_autosense = 1,
|
||||
ide_request_autosense = 2
|
||||
} ide_request_state;*/
|
||||
|
||||
// ide request
|
||||
typedef struct ide_qrequest {
|
||||
struct ide_qrequest *next;
|
||||
ide_device_info *device;
|
||||
scsi_ccb *request; // basic request
|
||||
|
||||
uint8 is_write : 1; // true for write request
|
||||
uint8 running : 1; // true if "on bus"
|
||||
uint8 uses_dma : 1; // true if using dma
|
||||
uint8 packet_irq : 1; // true if waiting for command packet irq
|
||||
uint8 queuable : 1; // true if command queuing is used
|
||||
uint8 tag; // command queuing tag
|
||||
} ide_qrequest;
|
||||
|
||||
|
||||
// state of ide bus
|
||||
typedef enum {
|
||||
ide_state_idle, // noone is using it, but overlapped
|
||||
// commands may be pending
|
||||
ide_state_accessing, // bus is in use
|
||||
ide_state_async_waiting, // waiting for IRQ, to be reported via irq_dpc
|
||||
ide_state_sync_waiting, // waiting for IRQ, to be reported via sync_wait_sem
|
||||
} ide_bus_state;
|
||||
|
||||
struct ide_bus_info {
|
||||
ide_qrequest *active_qrequest;
|
||||
|
||||
// controller
|
||||
ide_controller_interface *controller;
|
||||
ide_channel_cookie channel;
|
||||
|
||||
// lock, used for changes of bus state
|
||||
spinlock lock;
|
||||
cpu_status prev_irq_state;
|
||||
|
||||
ide_bus_state state; // current state of bus
|
||||
|
||||
benaphore status_report_ben; // to lock when you report XPT about bus state
|
||||
// i.e. during requeue, resubmit or finished
|
||||
|
||||
bool disconnected; // true, if controller is lost
|
||||
int num_running_reqs; // total number of running requests
|
||||
|
||||
scsi_bus scsi_cookie; // cookie for scsi bus
|
||||
|
||||
ide_bus_timer_info timer; // timeout
|
||||
scsi_dpc_cookie irq_dpc;
|
||||
ide_synced_pc *synced_pc_list;
|
||||
|
||||
sem_id sync_wait_sem; // released when sync_wait finished
|
||||
bool sync_wait_timeout; // true, if timeout occured
|
||||
|
||||
ide_device_info *active_device;
|
||||
ide_device_info *devices[2];
|
||||
ide_device_info *first_device;
|
||||
|
||||
ide_synced_pc scan_bus_syncinfo; // used to start bus scan
|
||||
sem_id scan_device_sem; // released when device has been scanned
|
||||
|
||||
ide_synced_pc disconnect_syncinfo; // used to handle lost controller
|
||||
|
||||
uchar path_id;
|
||||
|
||||
pnp_node_handle node; // our pnp node
|
||||
|
||||
// restrictions, read from controller node
|
||||
uint8 max_devices;
|
||||
uint8 can_DMA;
|
||||
uint8 can_CQ;
|
||||
|
||||
fast_log_handle log;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
|
||||
// call this before you change bus state
|
||||
#define IDE_LOCK( bus ) { \
|
||||
cpu_status prev_irq_state = disable_interrupts(); \
|
||||
acquire_spinlock( &bus->lock ); \
|
||||
bus->prev_irq_state = prev_irq_state; \
|
||||
}
|
||||
|
||||
// call this after you changed bus state
|
||||
#define IDE_UNLOCK( bus ) { \
|
||||
cpu_status prev_irq_state = bus->prev_irq_state; \
|
||||
release_spinlock( &bus->lock ); \
|
||||
restore_interrupts( prev_irq_state ); \
|
||||
}
|
||||
|
||||
// SIM interface
|
||||
#define IDE_SIM_MODULE_NAME "bus_managers/ide/sim/v1"
|
||||
|
||||
|
||||
enum {
|
||||
ev_ide_send_command = 1,
|
||||
ev_ide_device_start_service,
|
||||
ev_ide_device_start_service2,
|
||||
ev_ide_dpc_service,
|
||||
ev_ide_dpc_continue,
|
||||
ev_ide_irq_handle,
|
||||
ev_ide_cancel_irq_timeout,
|
||||
ev_ide_start_waiting,
|
||||
ev_ide_timeout_dpc,
|
||||
ev_ide_timeout,
|
||||
ev_ide_reset_bus,
|
||||
ev_ide_reset_device,
|
||||
|
||||
ev_ide_scsi_io,
|
||||
ev_ide_scsi_io_exec,
|
||||
ev_ide_scsi_io_invalid_device,
|
||||
ev_ide_scsi_io_bus_busy,
|
||||
ev_ide_scsi_io_device_busy,
|
||||
ev_ide_scsi_io_disconnected,
|
||||
ev_ide_finish_request,
|
||||
ev_ide_finish_norelease,
|
||||
|
||||
ev_ide_scan_device_int,
|
||||
ev_ide_scan_device_int_cant_send,
|
||||
ev_ide_scan_device_int_keeps_busy,
|
||||
ev_ide_scan_device_int_found
|
||||
};
|
||||
|
||||
|
||||
|
||||
// get selected device
|
||||
static inline
|
||||
ide_device_info *get_current_device(ide_bus_info *bus)
|
||||
{
|
||||
ide_task_file tf;
|
||||
|
||||
bus->controller->read_command_block_regs(bus->channel, &tf,
|
||||
ide_mask_device_head);
|
||||
|
||||
return bus->devices[tf.lba.device];
|
||||
}
|
||||
|
||||
|
||||
// check if device has released the bus
|
||||
// return: true, if bus was released
|
||||
static inline int
|
||||
device_released_bus(ide_device_info *device)
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
bus->controller->read_command_block_regs(bus->channel,
|
||||
&device->tf, ide_mask_sector_count);
|
||||
|
||||
return device->tf.queued.release;
|
||||
}
|
||||
|
||||
|
||||
// ata.c
|
||||
|
||||
bool check_rw_error(ide_device_info *device, ide_qrequest *qrequest);
|
||||
bool check_output(ide_device_info *device, bool drdy_required, int error_mask, bool is_write);
|
||||
|
||||
bool prep_ata(ide_device_info *device);
|
||||
void enable_CQ(ide_device_info *device, bool enable);
|
||||
void ata_send_rw(ide_device_info *device, ide_qrequest *qrequest,
|
||||
uint64 pos, size_t length, bool write);
|
||||
|
||||
void ata_dpc_DMA(ide_qrequest *qrequest);
|
||||
void ata_dpc_PIO(ide_qrequest *qrequest);
|
||||
|
||||
void ata_exec_io(ide_device_info *device, ide_qrequest *qrequest);
|
||||
|
||||
|
||||
// atapi.c
|
||||
|
||||
bool prep_atapi(ide_device_info *device);
|
||||
|
||||
void send_packet(ide_device_info *device, ide_qrequest *qrequest, bool write);
|
||||
void packet_dpc(ide_qrequest *qrequest);
|
||||
void atapi_exec_io(ide_device_info *device, ide_qrequest *qrequest);
|
||||
|
||||
|
||||
// basic_prot.c
|
||||
|
||||
bool ide_wait(ide_device_info *device, int mask, int not_mask, bool check_err,
|
||||
bigtime_t timeout);
|
||||
bool wait_for_drq(ide_device_info *device);
|
||||
bool wait_for_drqdown(ide_device_info *device);
|
||||
bool wait_for_drdy(ide_device_info *device);
|
||||
|
||||
// timeout in seconds
|
||||
bool send_command(ide_device_info *device, ide_qrequest *qrequest,
|
||||
bool need_drdy, uint32 timeout, ide_bus_state new_state);
|
||||
bool device_start_service( ide_device_info *device, int *tag);
|
||||
bool reset_device(ide_device_info *device, ide_qrequest *ignore);
|
||||
bool reset_bus(ide_device_info *device, ide_qrequest *ignore);
|
||||
|
||||
bool check_service_req(ide_device_info *device);
|
||||
|
||||
|
||||
// channel_mgr.c
|
||||
|
||||
extern ide_for_controller_interface ide_for_controller_module;
|
||||
|
||||
|
||||
// device_mgr.c
|
||||
|
||||
void scan_device_worker(ide_bus_info *bus, void *arg);
|
||||
|
||||
|
||||
// dma.c
|
||||
|
||||
bool prepare_dma(ide_device_info *device, ide_qrequest *qrequest);
|
||||
void start_dma(ide_device_info *device, ide_qrequest *qrequest);
|
||||
void start_dma_wait(ide_device_info *device, ide_qrequest *qrequest);
|
||||
void start_dma_wait_no_lock(ide_device_info *device, ide_qrequest *qrequest);
|
||||
bool finish_dma(ide_device_info *device);
|
||||
void abort_dma(ide_device_info *device, ide_qrequest *qrequest);
|
||||
|
||||
bool configure_dma(ide_device_info *device);
|
||||
|
||||
|
||||
// emulation.c
|
||||
|
||||
bool copy_sg_data(scsi_ccb *request, uint offset, uint req_size_limit,
|
||||
void *buffer, int size, bool to_buffer);
|
||||
void ide_request_sense(ide_device_info *device, ide_qrequest *qrequest);
|
||||
|
||||
|
||||
// pio.c
|
||||
|
||||
void prep_PIO_transfer(ide_device_info *device, ide_qrequest *qrequest);
|
||||
status_t read_PIO_block(ide_qrequest *qrequest, int length);
|
||||
status_t write_PIO_block(ide_qrequest *qrequest, int length);
|
||||
|
||||
|
||||
// queuing.c
|
||||
|
||||
bool send_abort_queue(ide_device_info *device);
|
||||
bool try_service(ide_device_info *device);
|
||||
|
||||
void reconnect_timeout_worker(ide_bus_info *bus, void *arg);
|
||||
int32 reconnect_timeout(timer *arg);
|
||||
|
||||
bool initialize_qreq_array(ide_device_info *device, int queue_depth);
|
||||
void destroy_qreq_array(ide_device_info *device);
|
||||
|
||||
|
||||
// sync.c
|
||||
|
||||
// timeout in seconds (according to CAM)
|
||||
void start_waiting(ide_bus_info *bus, uint32 timeout, int new_state);
|
||||
void start_waiting_nolock(ide_bus_info *bus, uint32 timeout, int new_state);
|
||||
void wait_for_sync(ide_bus_info *bus);
|
||||
void cancel_irq_timeout(ide_bus_info *bus);
|
||||
|
||||
status_t schedule_synced_pc(ide_bus_info *bus, ide_synced_pc *pc, void *arg);
|
||||
void init_synced_pc(ide_synced_pc *pc, ide_synced_pc_func func);
|
||||
void uninit_synced_pc(ide_synced_pc *pc);
|
||||
|
||||
void ide_dpc(void *arg);
|
||||
void access_finished(ide_bus_info *bus, ide_device_info *device);
|
||||
|
||||
status_t ide_irq_handler(ide_bus_info *bus, uint8 status);
|
||||
status_t ide_timeout(timer *arg);
|
||||
|
||||
|
||||
#endif
|
811
src/add-ons/kernel/bus_managers/ide/ide_sim.c
Normal file
811
src/add-ons/kernel/bus_managers/ide/ide_sim.c
Normal file
@ -0,0 +1,811 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Interface between ide bus manager and scsi bus manager.
|
||||
|
||||
The IDE bus manager has a bit unusual structure as it
|
||||
consists of a single level only. In fact it is no bus manager
|
||||
in terms of the PnP structure at all but a driver that maps
|
||||
one SCSI bus onto one IDE controller.
|
||||
|
||||
This structure does not allow us to publish IDE devices
|
||||
as they can be accessed via the SCSI bus node only. Therefore
|
||||
we do a full bus scan every time the IDE bus node is loaded.
|
||||
The drawback is that a bus rescan must be done indirectly via a
|
||||
SCSI bus scan.
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
|
||||
#include <bus/scsi/scsi_cmds.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
scsi_for_sim_interface *scsi;
|
||||
fast_log_info *fast_log;
|
||||
|
||||
|
||||
static fast_log_event_type ide_events[] =
|
||||
{
|
||||
{ ev_ide_send_command, "ev_ide_send_command " },
|
||||
{ ev_ide_device_start_service, "ev_ide_device_start_service" },
|
||||
{ ev_ide_device_start_service2, "ev_ide_device_start_service2" },
|
||||
{ ev_ide_dpc_service, "ev_ide_dpc_service" },
|
||||
{ ev_ide_dpc_continue, "ev_ide_dpc_continue" },
|
||||
{ ev_ide_irq_handle, "ev_ide_irq_handle" },
|
||||
{ ev_ide_cancel_irq_timeout, "ev_ide_cancel_irq_timeout" },
|
||||
{ ev_ide_start_waiting, "ev_ide_start_waiting" },
|
||||
{ ev_ide_timeout_dpc, "ev_ide_timeout_dpc" },
|
||||
{ ev_ide_timeout, "ev_ide_timeout" },
|
||||
{ ev_ide_reset_bus, "ev_ide_reset_bus" },
|
||||
{ ev_ide_reset_device, "ev_ide_reset_device" },
|
||||
|
||||
{ ev_ide_scsi_io, "ev_ide_scsi_io" },
|
||||
{ ev_ide_scsi_io_exec, "ev_ide_scsi_io_exec" },
|
||||
{ ev_ide_scsi_io_invalid_device, "ev_ide_scsi_io_invalid_device" },
|
||||
{ ev_ide_scsi_io_bus_busy, "ev_ide_scsi_io_bus_busy" },
|
||||
{ ev_ide_scsi_io_device_busy, "ev_ide_scsi_io_device_busy" },
|
||||
{ ev_ide_scsi_io_disconnected, "ev_ide_scsi_io_disconnected" },
|
||||
{ ev_ide_finish_request, "ev_ide_finish_request" },
|
||||
{ ev_ide_finish_norelease, "ev_ide_finish_norelease" },
|
||||
|
||||
{ ev_ide_scan_device_int, "ev_ide_scan_device_int" },
|
||||
{ ev_ide_scan_device_int_cant_send, "ev_ide_scan_device_int_cant_send" },
|
||||
{ ev_ide_scan_device_int_keeps_busy, "ev_ide_scan_device_int_keeps_busy" },
|
||||
{ ev_ide_scan_device_int_found, "ev_ide_scan_device_int_found" },
|
||||
{}
|
||||
};
|
||||
|
||||
static void disconnect_worker( ide_bus_info *bus, void *arg );
|
||||
static void set_check_condition( ide_qrequest *qrequest );
|
||||
|
||||
// check whether this request can be within device
|
||||
static inline bool is_queuable( ide_device_info *device, scsi_ccb *request )
|
||||
{
|
||||
int opcode = request->cdb[0];
|
||||
|
||||
// XXX disable queuing
|
||||
if( !device->CQ_enabled )
|
||||
return false;
|
||||
|
||||
// make sure the caller allows queuing
|
||||
if( (request->flags & SCSI_ORDERED_QTAG) != 0 )
|
||||
return false;
|
||||
|
||||
// for atapi, all commands could be queued, but all
|
||||
// atapi devices I know don't support queuing anyway
|
||||
if( opcode == SCSI_OP_READ_6 ||
|
||||
opcode == SCSI_OP_WRITE_6 ||
|
||||
opcode == SCSI_OP_READ_10 ||
|
||||
opcode == SCSI_OP_WRITE_10 )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sim_scsi_io(ide_bus_info *bus, scsi_ccb *request)
|
||||
{
|
||||
ide_device_info *device;
|
||||
bool queuable;
|
||||
ide_qrequest *qrequest;
|
||||
//ide_request_priv *priv;
|
||||
|
||||
// FAST_LOG3( bus->log, ev_ide_scsi_io, (uint32)request, request->target_id, request->target_lun );
|
||||
SHOW_FLOW(3, "%d:%d", request->target_id, request->target_lun);
|
||||
|
||||
if (bus->disconnected)
|
||||
goto err_disconnected;
|
||||
|
||||
// make sure, device is valid
|
||||
// I've read that there are ATAPI devices with more then one LUN,
|
||||
// but it seems that most (all?) devices ignore LUN, so we have
|
||||
// to restrict to LUN 0 to avoid mirror devices
|
||||
if (request->target_id >= 2)
|
||||
goto err_inv_device;
|
||||
|
||||
device = bus->devices[request->target_id];
|
||||
if (device == NULL)
|
||||
goto err_inv_device;
|
||||
|
||||
if (request->target_lun > device->last_lun)
|
||||
goto err_inv_device;
|
||||
|
||||
queuable = is_queuable(device, request);
|
||||
|
||||
// grab the bus
|
||||
ACQUIRE_BEN(&bus->status_report_ben);
|
||||
IDE_LOCK(bus);
|
||||
|
||||
if (bus->state != ide_state_idle)
|
||||
goto err_bus_busy;
|
||||
|
||||
// bail out if device can't accept further requests
|
||||
if (device->free_qrequests == NULL
|
||||
|| (device->num_running_reqs > 0 && !queuable))
|
||||
goto err_device_busy;
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
RELEASE_BEN(&bus->status_report_ben);
|
||||
|
||||
// as we own the bus, noone can bother us
|
||||
qrequest = device->free_qrequests;
|
||||
device->free_qrequests = qrequest->next;
|
||||
|
||||
qrequest->request = request;
|
||||
qrequest->queuable = queuable;
|
||||
qrequest->running = true;
|
||||
qrequest->uses_dma = false;
|
||||
|
||||
++device->num_running_reqs;
|
||||
++bus->num_running_reqs;
|
||||
bus->active_qrequest = qrequest;
|
||||
|
||||
FAST_LOGN(bus->log, ev_ide_scsi_io_exec, 4, (uint32)qrequest,
|
||||
(uint32)request, bus->num_running_reqs, device->num_running_reqs);
|
||||
|
||||
device->exec_io(device, qrequest);
|
||||
|
||||
return;
|
||||
|
||||
err_inv_device:
|
||||
SHOW_ERROR(3, "Invalid device %d:%d",
|
||||
request->target_id, request->target_lun);
|
||||
FAST_LOG1(bus->log, ev_ide_scsi_io_invalid_device, (uint32)request);
|
||||
|
||||
request->subsys_status = SCSI_SEL_TIMEOUT;
|
||||
scsi->finished(request, 1);
|
||||
return;
|
||||
|
||||
err_bus_busy:
|
||||
SHOW_FLOW0( 3, "Bus busy" );
|
||||
FAST_LOG1(bus->log, ev_ide_scsi_io_bus_busy, (uint32)request);
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
scsi->requeue(request, true);
|
||||
RELEASE_BEN(&bus->status_report_ben);
|
||||
return;
|
||||
|
||||
err_device_busy:
|
||||
SHOW_FLOW0( 3, "Device busy" );
|
||||
FAST_LOG1(bus->log, ev_ide_scsi_io_device_busy, (uint32)request);
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
scsi->requeue(request, false);
|
||||
RELEASE_BEN(&bus->status_report_ben);
|
||||
return;
|
||||
|
||||
err_disconnected:
|
||||
SHOW_ERROR0( 3, "No controller anymore" );
|
||||
FAST_LOG1(bus->log, ev_ide_scsi_io_disconnected, (uint32)request);
|
||||
request->subsys_status = SCSI_NO_HBA;
|
||||
scsi->finished(request, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_path_inquiry(ide_bus_info *bus, scsi_path_inquiry *info)
|
||||
{
|
||||
char *controller_name;
|
||||
|
||||
SHOW_FLOW0( 4, "" );
|
||||
|
||||
if( bus->disconnected )
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
info->hba_inquiry = SCSI_PI_TAG_ABLE | SCSI_PI_WIDE_16;
|
||||
|
||||
info->hba_misc = 0;
|
||||
|
||||
memset( info->vuhba_flags, 0, sizeof( info->vuhba_flags ));
|
||||
// we don't need any of the private data
|
||||
info->sim_priv = 0;
|
||||
|
||||
// there is no initiator for IDE, but SCSI needs it for scanning
|
||||
info->initiator_id = 2;
|
||||
// there's no controller limit, so set it higher then the maximum
|
||||
// number of queued requests, which is 32 per device * 2 devices
|
||||
info->hba_queue_size = 65;
|
||||
|
||||
strncpy( info->sim_vid, "OpenBeOS", SCSI_SIM_ID );
|
||||
|
||||
if( pnp->get_attr_string( bus->node,
|
||||
SCSI_DESCRIPTION_CONTROLLER_NAME, &controller_name, true ) == B_OK )
|
||||
{
|
||||
strncpy( info->hba_vid, controller_name, SCSI_HBA_ID );
|
||||
free( controller_name );
|
||||
} else
|
||||
strncpy( info->hba_vid, "", SCSI_HBA_ID );
|
||||
|
||||
strncpy( info->controller_family, "IDE", SCSI_FAM_ID );
|
||||
strncpy( info->controller_type, "IDE", SCSI_TYPE_ID );
|
||||
|
||||
SHOW_FLOW0( 4, "done" );
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scan_device(ide_bus_info *bus, int device)
|
||||
{
|
||||
SHOW_FLOW0(4, "");
|
||||
|
||||
// currently, the SCSI bus manager doesn't block the
|
||||
// bus when a bus or device scan is issued, so we
|
||||
// have to use a SPC for that to be sure no one else
|
||||
// is accessing the device or bus concurrently
|
||||
dprintf("1\n");
|
||||
schedule_synced_pc(bus, &bus->scan_bus_syncinfo, (void *)device);
|
||||
|
||||
dprintf("2\n");
|
||||
acquire_sem(bus->scan_device_sem);
|
||||
|
||||
dprintf("3\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_scan_bus(ide_bus_info *bus)
|
||||
{
|
||||
int i;
|
||||
|
||||
SHOW_FLOW0( 4, "" );
|
||||
|
||||
if (bus->disconnected)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
for (i = 0; i < bus->max_devices; ++i) {
|
||||
// ToDo: remove me! (this speeds up detection in bochs - because
|
||||
// timing seems to be broken there)
|
||||
if (i == 1) {
|
||||
dprintf("ignore 1!\n");
|
||||
continue;
|
||||
}
|
||||
scan_device(bus, i);
|
||||
}
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_abort(ide_bus_info *bus, scsi_ccb *ccb_to_abort)
|
||||
{
|
||||
// we cannot abort specific commands, so just ignore
|
||||
if (bus->disconnected)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_term_io(ide_bus_info *bus, scsi_ccb *ccb_to_abort)
|
||||
{
|
||||
// we cannot terminate commands, so just ignore
|
||||
if (bus->disconnected)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_reset_bus(ide_bus_info *bus)
|
||||
{
|
||||
// no, we don't do that
|
||||
if (bus->disconnected)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
return SCSI_REQ_INVALID;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
sim_reset_device(ide_bus_info *bus, uchar target_id, uchar target_lun)
|
||||
{
|
||||
// xxx to do
|
||||
if (bus->disconnected)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
return SCSI_REQ_INVALID;
|
||||
}
|
||||
|
||||
|
||||
/** fill sense buffer according to device sense */
|
||||
|
||||
void
|
||||
create_sense(ide_device_info *device, scsi_sense *sense)
|
||||
{
|
||||
memset(sense, 0, sizeof(*sense));
|
||||
|
||||
sense->error_code = SCSIS_CURR_ERROR;
|
||||
sense->sense_key = decode_sense_key(device->combined_sense);
|
||||
sense->add_sense_length = sizeof(*sense) - 7;
|
||||
sense->asc = decode_sense_asc(device->combined_sense);
|
||||
sense->ascq = decode_sense_ascq(device->combined_sense);
|
||||
sense->sense_key_spec.raw.SKSV = 0; // no additional info
|
||||
}
|
||||
|
||||
|
||||
/** finish command, updating sense of device and request, and release bus */
|
||||
|
||||
void
|
||||
finish_checksense(ide_qrequest *qrequest)
|
||||
{
|
||||
SHOW_FLOW( 3, "%p, subsys_status=%d, sense=%x",
|
||||
qrequest->request,
|
||||
qrequest->request->subsys_status,
|
||||
(int)qrequest->device->new_combined_sense );
|
||||
|
||||
qrequest->request->subsys_status = qrequest->device->subsys_status;
|
||||
|
||||
if (qrequest->request->subsys_status == SCSI_REQ_CMP) {
|
||||
// device or emulation code completed command
|
||||
qrequest->device->combined_sense = qrequest->device->new_combined_sense;
|
||||
|
||||
// if emulation code detected error, set CHECK CONDITION
|
||||
if (qrequest->device->combined_sense)
|
||||
set_check_condition(qrequest);
|
||||
}
|
||||
|
||||
finish_request(qrequest, false);
|
||||
}
|
||||
|
||||
|
||||
/** finish request and release bus
|
||||
* resubmit - true, if request should be resubmitted by XPT
|
||||
*/
|
||||
|
||||
void
|
||||
finish_request(ide_qrequest *qrequest, bool resubmit)
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
ide_bus_info *bus = device->bus;
|
||||
scsi_ccb *request;
|
||||
uint num_running;
|
||||
|
||||
FAST_LOG2(bus->log, ev_ide_finish_request, (uint32)qrequest, resubmit);
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
// save request first, as qrequest can be reused as soon as
|
||||
// access_finished is called!
|
||||
request = qrequest->request;
|
||||
|
||||
qrequest->running = false;
|
||||
qrequest->next = device->free_qrequests;
|
||||
device->free_qrequests = qrequest;
|
||||
|
||||
// num_running is not really correct as the XPT is interested
|
||||
// in the number of concurrent requests when it was *started* !
|
||||
num_running = device->num_running_reqs--;
|
||||
--bus->num_running_reqs;
|
||||
|
||||
// paranoia
|
||||
bus->active_qrequest = NULL;
|
||||
|
||||
// release bus, handling service requests;
|
||||
// TBD:
|
||||
// if we really handle a service request, the finished command
|
||||
// is delayed unnecessarily, but if we tell the XPT about the finished
|
||||
// command first, it will instantly try to pass us another
|
||||
// request to handle, which we will refuse as the bus is still
|
||||
// locked; this really has to be improved
|
||||
access_finished(bus, device);
|
||||
|
||||
ACQUIRE_BEN(&bus->status_report_ben);
|
||||
|
||||
if (resubmit)
|
||||
scsi->resubmit(request);
|
||||
else
|
||||
scsi->finished(request, num_running);
|
||||
|
||||
RELEASE_BEN(&bus->status_report_ben);
|
||||
}
|
||||
|
||||
|
||||
/** set CHECK CONDITION of device and perform auto-sense if requested.
|
||||
* (don't move it before finish_request - we don't want to inline
|
||||
* it as it's on the rarely used error path)
|
||||
*/
|
||||
|
||||
static void
|
||||
set_check_condition(ide_qrequest *qrequest)
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
ide_device_info *device = qrequest->device;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
request->subsys_status = SCSI_REQ_CMP_ERR;
|
||||
request->device_status = SCSI_STATUS_CHECK_CONDITION;
|
||||
|
||||
// copy sense only if caller requested it
|
||||
if ((request->flags & SCSI_DIS_AUTOSENSE) == 0) {
|
||||
scsi_sense sense;
|
||||
int sense_len;
|
||||
|
||||
SHOW_FLOW0( 3, "autosense" );
|
||||
|
||||
// we cannot copy sense directly as sense buffer may be too small
|
||||
create_sense(device, &sense);
|
||||
|
||||
sense_len = min(SCSI_MAX_SENSE_SIZE, sizeof(sense));
|
||||
|
||||
memcpy(request->sense, &sense, sense_len);
|
||||
request->sense_resid = SCSI_MAX_SENSE_SIZE - sense_len;
|
||||
request->subsys_status |= SCSI_AUTOSNS_VALID;
|
||||
|
||||
// device sense gets reset once it's read
|
||||
device->combined_sense = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
finish_retry(ide_qrequest *qrequest)
|
||||
{
|
||||
qrequest->device->combined_sense = 0;
|
||||
finish_request(qrequest, true);
|
||||
}
|
||||
|
||||
|
||||
/** finish request and abort pending requests of the device
|
||||
* (to be called when the request failed and thus messed up the queue)
|
||||
*/
|
||||
|
||||
void
|
||||
finish_reset_queue(ide_qrequest *qrequest)
|
||||
{
|
||||
ide_bus_info *bus = qrequest->device->bus;
|
||||
|
||||
// don't remove block_bus!!!
|
||||
// during finish_checksense, the bus is released, so
|
||||
// the SCSI bus manager could send us further commands
|
||||
scsi->block_bus(bus->scsi_cookie);
|
||||
|
||||
finish_checksense(qrequest);
|
||||
send_abort_queue(qrequest->device);
|
||||
|
||||
scsi->unblock_bus(bus->scsi_cookie);
|
||||
}
|
||||
|
||||
|
||||
/** finish request, but don't release bus
|
||||
* if resubmit is true, the request will be resubmitted
|
||||
*/
|
||||
|
||||
static void
|
||||
finish_norelease(ide_qrequest *qrequest, bool resubmit)
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint num_requests;
|
||||
|
||||
FAST_LOG2(bus->log, ev_ide_finish_norelease, (uint32)qrequest, resubmit);
|
||||
|
||||
qrequest->running = false;
|
||||
qrequest->next = device->free_qrequests;
|
||||
device->free_qrequests = qrequest;
|
||||
|
||||
num_requests = device->num_running_reqs++;
|
||||
--bus->num_running_reqs;
|
||||
|
||||
if (bus->active_qrequest == qrequest)
|
||||
bus->active_qrequest = NULL;
|
||||
|
||||
ACQUIRE_BEN(&bus->status_report_ben);
|
||||
|
||||
if (resubmit)
|
||||
scsi->resubmit(qrequest->request);
|
||||
else
|
||||
scsi->finished(qrequest->request, num_requests);
|
||||
|
||||
RELEASE_BEN(&bus->status_report_ben);
|
||||
}
|
||||
|
||||
|
||||
/** finish all queued requests but <ignore> of the device;
|
||||
* set resubmit, if requests are to be resubmitted by xpt
|
||||
*/
|
||||
|
||||
void
|
||||
finish_all_requests(ide_device_info *device, ide_qrequest *ignore,
|
||||
int subsys_status, bool resubmit)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (device == NULL)
|
||||
return;
|
||||
|
||||
// we only have to block the device, but for CD changers we
|
||||
// have to block all LUNS of the device (and we neither know
|
||||
// their device handle nor which exist at all), so block
|
||||
// the entire bus instead (it won't take that long anyway)
|
||||
scsi->block_bus(device->bus->scsi_cookie);
|
||||
|
||||
for (i = 0; i < device->queue_depth; ++i) {
|
||||
ide_qrequest *qrequest = &device->qreq_array[i];
|
||||
|
||||
if (qrequest->running && qrequest != ignore) {
|
||||
qrequest->request->subsys_status = subsys_status;
|
||||
finish_norelease(qrequest, resubmit);
|
||||
}
|
||||
}
|
||||
|
||||
scsi->unblock_bus(device->bus->scsi_cookie);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ide_sim_init_bus(pnp_node_handle node, void *user_cookie, void **cookie)
|
||||
{
|
||||
ide_bus_info *bus;
|
||||
int res;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
// first prepare the info structure
|
||||
bus = (ide_bus_info *)malloc(sizeof(*bus));
|
||||
if (bus == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset( bus, 0, sizeof( *bus ));
|
||||
bus->node = node;
|
||||
bus->lock = 0;
|
||||
bus->num_running_reqs = 0;
|
||||
bus->active_qrequest = NULL;
|
||||
bus->disconnected = false;
|
||||
|
||||
{
|
||||
int32 channel_id = -1;
|
||||
|
||||
pnp->get_attr_uint32( node, IDE_CHANNEL_ID_ITEM, (uint32 *)&channel_id, true );
|
||||
|
||||
sprintf( bus->name, "ide_bus %d", (int)channel_id );
|
||||
}
|
||||
|
||||
bus->log = fast_log->start_log( bus->name, ide_events );
|
||||
if( bus->log == NULL ) {
|
||||
res = B_NO_MEMORY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
init_synced_pc( &bus->scan_bus_syncinfo, scan_device_worker );
|
||||
init_synced_pc( &bus->disconnect_syncinfo, disconnect_worker );
|
||||
|
||||
bus->scsi_cookie = user_cookie;
|
||||
|
||||
bus->state = ide_state_idle;
|
||||
|
||||
bus->timer.bus = bus;
|
||||
|
||||
bus->synced_pc_list = NULL;
|
||||
if( (res = scsi->alloc_dpc( &bus->irq_dpc )) < 0 )
|
||||
goto err1;
|
||||
|
||||
bus->active_device = NULL;
|
||||
bus->sync_wait_sem = create_sem( 0, "ide_sync_wait" );
|
||||
if( bus->sync_wait_sem < 0 ) {
|
||||
res = bus->sync_wait_sem;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
bus->devices[0] = bus->devices[1] = NULL;
|
||||
|
||||
bus->scan_device_sem = create_sem( 0, "ide_scan_finished" );
|
||||
if( bus->scan_device_sem < 0 ) {
|
||||
res = bus->scan_device_sem;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
res = INIT_BEN( &bus->status_report_ben, "ide_status_report" );
|
||||
if( res < 0 )
|
||||
goto err4;
|
||||
|
||||
bus->first_device = NULL;
|
||||
|
||||
// read restrictions of controller
|
||||
|
||||
if( pnp->get_attr_uint8( node,
|
||||
IDE_CONTROLLER_MAX_DEVICES_ITEM, &bus->max_devices, true ) != B_OK )
|
||||
// per default, 2 devices are supported per node
|
||||
bus->max_devices = 2;
|
||||
|
||||
bus->max_devices = min( bus->max_devices, 2 );
|
||||
|
||||
if (pnp->get_attr_uint8(node, IDE_CONTROLLER_CAN_DMA_ITEM, &bus->can_DMA, true) != B_OK)
|
||||
// per default, no dma support
|
||||
bus->can_DMA = false;
|
||||
|
||||
SHOW_FLOW(2, "can_dma: %d", bus->can_DMA);
|
||||
|
||||
if (pnp->get_attr_uint8(node, IDE_CONTROLLER_CAN_CQ_ITEM, &bus->can_CQ, true) != B_OK)
|
||||
// per default, command queuing is supported unless the driver
|
||||
// reports problems (queuing should be transparent to
|
||||
// controller, but for sure there is some buggy, over-optimizing
|
||||
// controller out there)
|
||||
bus->can_CQ = true;
|
||||
|
||||
res = pnp->load_driver(pnp->get_parent(node), bus,
|
||||
(pnp_driver_info **)&bus->controller,
|
||||
(void **)&bus->channel);
|
||||
if (res != B_OK)
|
||||
goto err5;
|
||||
|
||||
*cookie = bus;
|
||||
|
||||
// detect devices
|
||||
sim_scan_bus(bus);
|
||||
|
||||
return B_OK;
|
||||
|
||||
err5:
|
||||
DELETE_BEN(&bus->status_report_ben);
|
||||
err4:
|
||||
delete_sem(bus->scan_device_sem);
|
||||
err3:
|
||||
delete_sem(bus->sync_wait_sem);
|
||||
|
||||
err2:
|
||||
scsi->free_dpc(bus->irq_dpc);
|
||||
|
||||
err1:
|
||||
uninit_synced_pc(&bus->scan_bus_syncinfo);
|
||||
uninit_synced_pc(&bus->disconnect_syncinfo);
|
||||
fast_log->stop_log(bus->log);
|
||||
|
||||
err:
|
||||
free(bus);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ide_sim_uninit_bus(ide_bus_info *bus)
|
||||
{
|
||||
pnp->unload_driver(pnp->get_parent(bus->node));
|
||||
|
||||
DELETE_BEN(&bus->status_report_ben);
|
||||
delete_sem(bus->scan_device_sem);
|
||||
delete_sem(bus->sync_wait_sem);
|
||||
scsi->free_dpc(bus->irq_dpc);
|
||||
uninit_synced_pc(&bus->scan_bus_syncinfo);
|
||||
uninit_synced_pc(&bus->disconnect_syncinfo);
|
||||
fast_log->stop_log(bus->log);
|
||||
|
||||
free(bus);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// abort all running requests with SCSI_NO_HBA; finally, unblock bus
|
||||
static void
|
||||
disconnect_worker(ide_bus_info *bus, void *arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bus->max_devices; ++i) {
|
||||
if (bus->devices[i])
|
||||
// is this the proper error code?
|
||||
finish_all_requests(bus->devices[i], NULL, SCSI_NO_HBA, false);
|
||||
}
|
||||
|
||||
scsi->unblock_bus(bus->scsi_cookie);
|
||||
}
|
||||
|
||||
|
||||
static void ide_sim_bus_removed( pnp_node_handle node, ide_bus_info *bus )
|
||||
{
|
||||
if( bus == NULL )
|
||||
// driver not loaded - no manual intervention needed
|
||||
return;
|
||||
|
||||
// XPT must not issue further commands
|
||||
scsi->block_bus( bus->scsi_cookie );
|
||||
// make sure, we refuse all new commands
|
||||
bus->disconnected = true;
|
||||
// abort all running commands with SCSI_NO_HBA
|
||||
// (the scheduled function also unblocks the bus when finished)
|
||||
schedule_synced_pc( bus, &bus->disconnect_syncinfo, NULL );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ide_sim_get_restrictions(ide_bus_info *bus, uchar target_id,
|
||||
bool *is_atapi, bool *no_autosense, uint32 *max_blocks)
|
||||
{
|
||||
ide_device_info *device = bus->devices[target_id];
|
||||
|
||||
// we declare even ATA devices as ATAPI so we have to emulate fewer
|
||||
// commands
|
||||
*is_atapi = true;
|
||||
|
||||
// we emulate autosense for ATA devices
|
||||
*no_autosense = false;
|
||||
|
||||
if( device != NULL && device->is_atapi ) {
|
||||
// we don't support native autosense for ATAPI devices
|
||||
*no_autosense = true;
|
||||
}
|
||||
|
||||
*max_blocks = 255;
|
||||
|
||||
if( device->is_atapi ) {
|
||||
if( strncmp( device->infoblock.model_number, "IOMEGA ZIP 100 ATAPI",
|
||||
strlen( "IOMEGA ZIP 100 ATAPI" )) == 0 ||
|
||||
strncmp( device->infoblock.model_number, "IOMEGA Clik!",
|
||||
strlen( "IOMEGA Clik!" )) == 0 )
|
||||
{
|
||||
SHOW_ERROR0( 2, "Found buggy ZIP/Clik! drive - restricting transmission size" );
|
||||
*max_blocks = 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t std_ops( int32 op, ... )
|
||||
{
|
||||
switch( op ) {
|
||||
case B_MODULE_INIT:
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{ FAST_LOG_MODULE_NAME, (module_info **)&fast_log },
|
||||
{ SCSI_FOR_SIM_MODULE_NAME, (module_info **)&scsi },
|
||||
{ DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
scsi_sim_interface ide_sim_module = {
|
||||
{
|
||||
{
|
||||
IDE_SIM_MODULE_NAME,
|
||||
0,
|
||||
std_ops,
|
||||
},
|
||||
|
||||
(status_t (*)( pnp_node_handle, void *, void **))ide_sim_init_bus,
|
||||
(status_t (*)( void *)) ide_sim_uninit_bus,
|
||||
|
||||
NULL,
|
||||
(void (*)( pnp_node_handle , void *)) ide_sim_bus_removed
|
||||
},
|
||||
|
||||
(void (*)( scsi_sim_cookie, scsi_ccb * )) sim_scsi_io,
|
||||
(uchar (*)( scsi_sim_cookie, scsi_ccb * )) sim_abort,
|
||||
(uchar (*)( scsi_sim_cookie, uchar, uchar )) sim_reset_device,
|
||||
(uchar (*)( scsi_sim_cookie, scsi_ccb * )) sim_term_io,
|
||||
|
||||
(uchar (*)( scsi_sim_cookie, scsi_path_inquiry * )) sim_path_inquiry,
|
||||
(uchar (*)( scsi_sim_cookie )) sim_scan_bus,
|
||||
(uchar (*)( scsi_sim_cookie )) sim_reset_bus,
|
||||
|
||||
(void (*)( scsi_sim_cookie, uchar,
|
||||
bool*, bool *, uint32 *)) ide_sim_get_restrictions
|
||||
};
|
78
src/add-ons/kernel/bus_managers/ide/ide_sim.h
Normal file
78
src/add-ons/kernel/bus_managers/ide/ide_sim.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Interface between ide and scsi bus manager
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __IDE_SIM_H__
|
||||
#define __IDE_SIM_H__
|
||||
|
||||
|
||||
#include "bus/scsi/scsi_cmds.h"
|
||||
|
||||
extern scsi_for_sim_interface *scsi;
|
||||
extern scsi_sim_interface ide_sim_module;
|
||||
|
||||
// set sense of current request
|
||||
static inline void
|
||||
set_sense(ide_device_info *device, int sense_key, int sense_asc)
|
||||
{
|
||||
device->new_combined_sense = (sense_key << 16) | sense_asc;
|
||||
}
|
||||
|
||||
// retrieve key from combined sense
|
||||
static inline uint8
|
||||
decode_sense_key(uint32 combined_sense)
|
||||
{
|
||||
return (combined_sense >> 16) & 0xff;
|
||||
}
|
||||
|
||||
// retrieve asc from combined sense
|
||||
static inline uint8
|
||||
decode_sense_asc(uint32 combined_sense)
|
||||
{
|
||||
return (combined_sense >> 8) & 0xff;
|
||||
}
|
||||
|
||||
// retrieve ascq from combined sense
|
||||
static inline uint8
|
||||
decode_sense_ascq(uint32 combined_sense)
|
||||
{
|
||||
return combined_sense & 0xff;
|
||||
}
|
||||
|
||||
// retrieve asc and ascq from combined sense
|
||||
static inline uint16
|
||||
decode_sense_asc_ascq(uint32 combined_sense)
|
||||
{
|
||||
return combined_sense & 0xffff;
|
||||
}
|
||||
|
||||
void finish_request(ide_qrequest *qrequest, bool resubmit);
|
||||
void finish_reset_queue(ide_qrequest *qrequest);
|
||||
void finish_retry(ide_qrequest *qrequest);
|
||||
void finish_all_requests(ide_device_info *device, ide_qrequest *ignore,
|
||||
int subsys_status, bool resubmit);
|
||||
void finish_checksense(ide_qrequest *qrequest);
|
||||
|
||||
|
||||
// start request by resetting sense
|
||||
static inline void
|
||||
start_request(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
device->new_combined_sense = 0;
|
||||
device->subsys_status = SCSI_REQ_CMP;
|
||||
qrequest->request->device_status = SCSI_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
void create_sense(ide_device_info *device, scsi_sense *sense);
|
||||
|
||||
|
||||
#endif
|
350
src/add-ons/kernel/bus_managers/ide/pio.c
Normal file
350
src/add-ons/kernel/bus_managers/ide/pio.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
** Copyright 2002-04, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
PIO data transmission
|
||||
|
||||
This file is more difficult then you might expect as the SCSI system
|
||||
uses physical addresses everywhere which have to be mapped into
|
||||
virtual address space during transmission. Additionally, during ATAPI
|
||||
commands we may have to transmit more data then exist because the
|
||||
data len specified by the command doesn't need to be the same as
|
||||
of the data buffer provided.
|
||||
|
||||
The handling of S/G entries of odd size may look superfluous as the
|
||||
SCSI bus manager can take care of that. In general, this would be possible
|
||||
as most controllers need even alignment for DMA as well, but some can
|
||||
handle _any_ S/G list and it wouldn't be sensitive to enforce stricter
|
||||
alignement just for some rare PIO transmissions.
|
||||
|
||||
Little hint for the meaning of "transferred": this is the number of bytes
|
||||
sent over the bus. For read-transmissions, this may be one more then copied
|
||||
into the buffer (the extra byte read is stored in device->odd_byte), for
|
||||
write-transmissions, this may be one less (the waiting byte is pending in
|
||||
device->odd_byte).
|
||||
|
||||
In terms of error handling: we don't bother checking transmission of every
|
||||
single byte via read/write_pio(). At least at the end of the request, when
|
||||
the status bits are verified, we will see that something has gone wrong.
|
||||
|
||||
TBD: S/G entries may have odd start address. For non-Intel architecture
|
||||
we either have to copy data to an aligned buffer or have to modify
|
||||
PIO-handling in controller drivers.
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "KernelExport_ext.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ide_sim.h"
|
||||
|
||||
// internal error code if scatter gather table is too short
|
||||
#define ERR_TOO_BIG (B_ERRORS_END + 1)
|
||||
|
||||
// prepare PIO transfer
|
||||
void prep_PIO_transfer( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
SHOW_FLOW0( 4, "" );
|
||||
|
||||
device->left_sg_elem = qrequest->request->sg_cnt;
|
||||
device->cur_sg_elem = qrequest->request->sg_list;
|
||||
device->cur_sg_ofs = 0;
|
||||
device->has_odd_byte = false;
|
||||
qrequest->request->data_resid = qrequest->request->data_len;
|
||||
}
|
||||
|
||||
// transfer virtually continuous data
|
||||
static inline status_t transfer_PIO_virtcont( ide_device_info *device, char *virt_addr, int length,
|
||||
bool write, int *transferred )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
ide_controller_interface *controller = bus->controller;
|
||||
ide_channel_cookie cookie = bus->channel;
|
||||
|
||||
if( write ) {
|
||||
// if there is a byte left from last chunk, transmit it together
|
||||
// with the first byte of the current chunk (IDE requires 16 bits
|
||||
// to be transmitted at once)
|
||||
if( device->has_odd_byte ) {
|
||||
uint8 buffer[2];
|
||||
|
||||
buffer[0] = device->odd_byte;
|
||||
buffer[1] = *virt_addr++;
|
||||
|
||||
controller->write_pio( cookie, (uint16 *)buffer, 1, false );
|
||||
|
||||
--length;
|
||||
*transferred += 2;
|
||||
}
|
||||
|
||||
controller->write_pio( cookie, (uint16 *)virt_addr, length / 2, false );
|
||||
|
||||
// take care if chunk size was odd, which means that 1 byte remains
|
||||
virt_addr += length & ~1;
|
||||
*transferred += length & ~1;
|
||||
|
||||
device->has_odd_byte = (length & 1) != 0;
|
||||
|
||||
if( device->has_odd_byte )
|
||||
device->odd_byte = *virt_addr;
|
||||
|
||||
} else {
|
||||
// if we read one byte too much last time, push it into current chunk
|
||||
if( device->has_odd_byte ) {
|
||||
*virt_addr++ = device->odd_byte;
|
||||
--length;
|
||||
}
|
||||
|
||||
SHOW_FLOW( 4, "Reading PIO to %p, %d bytes", virt_addr, length );
|
||||
|
||||
controller->read_pio( cookie, (uint16 *)virt_addr, length / 2, false );
|
||||
|
||||
// take care of odd chunk size;
|
||||
// in this case we read 1 byte to few!
|
||||
virt_addr += length & ~1;
|
||||
*transferred += length & ~1;
|
||||
|
||||
device->has_odd_byte = (length & 1) != 0;
|
||||
|
||||
if( device->has_odd_byte ) {
|
||||
uint8 buffer[2];
|
||||
|
||||
// now read the missing byte; as we have to read 2 bytes at once,
|
||||
// we'll read one byte too much
|
||||
controller->read_pio( cookie, (uint16 *)buffer, 1, false );
|
||||
|
||||
*virt_addr = buffer[0];
|
||||
device->odd_byte = buffer[1];
|
||||
|
||||
*transferred += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// transmit physically continuous data
|
||||
static inline status_t
|
||||
transfer_PIO_physcont(ide_device_info *device, addr_t phys_addr,
|
||||
int length, bool write, int *transferred)
|
||||
{
|
||||
// we must split up chunk into B_PAGE_SIZE blocks as we can map only
|
||||
// one page into address space at once
|
||||
while( length > 0 ) {
|
||||
void *virt_addr;
|
||||
int page_left, cur_len;
|
||||
status_t err;
|
||||
|
||||
SHOW_FLOW( 4, "Transmitting to/from physical address %x, %d bytes left", (int)phys_addr,
|
||||
length );
|
||||
|
||||
if (map_mainmemory(phys_addr, &virt_addr) != B_OK) {
|
||||
// ouch: this should never ever happen
|
||||
set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// if chunks starts in the middle of a page, we have even less then
|
||||
// a page left
|
||||
page_left = B_PAGE_SIZE - phys_addr % B_PAGE_SIZE;
|
||||
|
||||
SHOW_FLOW( 4, "page_left=%d", page_left );
|
||||
|
||||
cur_len = min( page_left, length );
|
||||
|
||||
SHOW_FLOW( 4, "cur_len=%d", cur_len );
|
||||
|
||||
err = transfer_PIO_virtcont( device, (char *)virt_addr,
|
||||
cur_len, write, transferred );
|
||||
|
||||
if( err != B_OK ) {
|
||||
unmap_mainmemory( virt_addr );
|
||||
return err;
|
||||
}
|
||||
|
||||
length -= cur_len;
|
||||
phys_addr += cur_len;
|
||||
|
||||
unmap_mainmemory( virt_addr );
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// transfer PIO block from/to buffer
|
||||
static inline int transfer_PIO_block( ide_device_info *device, int length, bool write,
|
||||
int *transferred )
|
||||
{
|
||||
// data is usually split up into multiple scatter/gather blocks
|
||||
while( length > 0 ) {
|
||||
int left_bytes, cur_len;
|
||||
status_t err;
|
||||
|
||||
if( device->left_sg_elem == 0 )
|
||||
// ups - buffer too small (for ATAPI data, this is OK)
|
||||
return ERR_TOO_BIG;
|
||||
|
||||
// we might have transmitted part of a scatter/entry already!
|
||||
left_bytes = device->cur_sg_elem->size - device->cur_sg_ofs;
|
||||
|
||||
cur_len = min( left_bytes, length );
|
||||
|
||||
err = transfer_PIO_physcont( device,
|
||||
(addr_t)device->cur_sg_elem->address + device->cur_sg_ofs,
|
||||
cur_len, write, transferred );
|
||||
|
||||
if( err != B_OK )
|
||||
return err;
|
||||
|
||||
if( left_bytes <= length ) {
|
||||
// end of one scatter/gather block reached
|
||||
device->cur_sg_ofs = 0;
|
||||
++device->cur_sg_elem;
|
||||
--device->left_sg_elem;
|
||||
} else {
|
||||
// still in the same block
|
||||
device->cur_sg_ofs += cur_len;
|
||||
}
|
||||
|
||||
length -= cur_len;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// write zero data (required for ATAPI if we ran out of data)
|
||||
static void write_discard_PIO( ide_device_info *device, int length )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint8 buffer[32];
|
||||
|
||||
memset( buffer, 0, sizeof( buffer ));
|
||||
|
||||
// we transmit 32 zero-bytes at once
|
||||
// (not very efficient but easy to implement - you get what you deserve
|
||||
// when you don't provide enough buffer)
|
||||
while( length > 0 ) {
|
||||
int cur_len;
|
||||
|
||||
// if device asks for odd number of bytes, append an extra byte to
|
||||
// make length even (this is the "length + 1" term)
|
||||
cur_len = min( length + 1, (int)(sizeof( buffer ))) / 2;
|
||||
|
||||
bus->controller->write_pio( bus->channel, (uint16 *)buffer, cur_len, false );
|
||||
|
||||
length -= cur_len * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// read PIO data and discard it (required for ATAPI if buffer was too small)
|
||||
static void read_discard_PIO( ide_device_info *device, int length )
|
||||
{
|
||||
ide_bus_info *bus = device->bus;
|
||||
uint8 buffer[32];
|
||||
|
||||
// discard 32 bytes at once (see write_discard_PIO)
|
||||
while( length > 0 ) {
|
||||
int cur_len;
|
||||
|
||||
// read extra byte if length is odd (that's the "length + 1")
|
||||
cur_len = min( length + 1, (int)sizeof( buffer )) / 2;
|
||||
|
||||
bus->controller->read_pio( bus->channel, (uint16 *)buffer, cur_len, false );
|
||||
|
||||
length -= cur_len * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// write PIO data
|
||||
// return: there are 3 possible results
|
||||
// NO_ERROR - everything's nice and groovy
|
||||
// ERR_TOO_BIG - data buffer was too short, remaining data got discarded
|
||||
// B_ERROR - something serious went wrong, sense data was set
|
||||
status_t write_PIO_block( ide_qrequest *qrequest, int length )
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
int transferred;
|
||||
status_t err;
|
||||
|
||||
transferred = 0;
|
||||
err = transfer_PIO_block( device, length, true, &transferred );
|
||||
|
||||
qrequest->request->data_resid -= transferred;
|
||||
|
||||
if( err != ERR_TOO_BIG )
|
||||
return err;
|
||||
|
||||
// there may be a pending odd byte - transmit that now
|
||||
if( qrequest->device->has_odd_byte ) {
|
||||
uint8 buffer[2];
|
||||
|
||||
buffer[0] = device->odd_byte;
|
||||
buffer[1] = 0;
|
||||
|
||||
device->has_odd_byte = false;
|
||||
|
||||
qrequest->request->data_resid -= 1;
|
||||
transferred += 2;
|
||||
|
||||
device->bus->controller->write_pio( device->bus->channel, (uint16 *)buffer, 1, false );
|
||||
}
|
||||
|
||||
// "transferred" may actually be larger then length because the last odd-byte
|
||||
// is sent together with an extra zero-byte
|
||||
if( transferred >= length )
|
||||
return err;
|
||||
|
||||
// Ouch! the device asks for data but we haven't got any left.
|
||||
// Sadly, this behaviour is OK for ATAPI packets, but there is no
|
||||
// way to tell the device that we don't have any data left;
|
||||
// only solution is to send zero bytes, though it's BAD BAD BAD
|
||||
write_discard_PIO( qrequest->device, length - transferred );
|
||||
return ERR_TOO_BIG;
|
||||
}
|
||||
|
||||
|
||||
// read PIO data
|
||||
// return: see write_PIO_block
|
||||
status_t read_PIO_block( ide_qrequest *qrequest, int length )
|
||||
{
|
||||
ide_device_info *device = qrequest->device;
|
||||
int transferred;
|
||||
status_t err;
|
||||
|
||||
transferred = 0;
|
||||
err = transfer_PIO_block( qrequest->device, length, false, &transferred );
|
||||
|
||||
qrequest->request->data_resid -= transferred;
|
||||
|
||||
// if length was odd, there's an extra byte waiting in device->odd_byte
|
||||
if( device->has_odd_byte ) {
|
||||
// discard byte
|
||||
device->has_odd_byte = false;
|
||||
// adjust res_id as the extra byte didn't reach the buffer
|
||||
++qrequest->request->data_resid;
|
||||
}
|
||||
|
||||
if( err != ERR_TOO_BIG )
|
||||
return err;
|
||||
|
||||
// the device returns more data then the buffer can store;
|
||||
// for ATAPI this is OK - we just discard remaining bytes (there
|
||||
// is no way to tell ATAPI about that, but we "only" waste time)
|
||||
|
||||
// perhaps discarding the extra odd-byte was sufficient
|
||||
if( transferred >= length )
|
||||
return err;
|
||||
|
||||
SHOW_FLOW( 3, "discarding after %d bytes", transferred );
|
||||
read_discard_PIO( qrequest->device, length - transferred );
|
||||
return ERR_TOO_BIG;
|
||||
}
|
374
src/add-ons/kernel/bus_managers/ide/queuing.c
Normal file
374
src/add-ons/kernel/bus_managers/ide/queuing.c
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Command queuing functions
|
||||
*/
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
#include "ide_cmds.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
|
||||
// maximum number of errors until command queuing is disabled
|
||||
#define MAX_CQ_FAILURES 3
|
||||
|
||||
|
||||
/** convert tag to request */
|
||||
|
||||
static inline ide_qrequest *
|
||||
tag2request(ide_device_info *device, int tag)
|
||||
{
|
||||
ide_qrequest *qrequest = &device->qreq_array[tag];
|
||||
|
||||
if (qrequest->running)
|
||||
return qrequest;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/** service device
|
||||
*
|
||||
* (expects locked bus and bus in "accessing" state)
|
||||
* returns true if servicing a command (implies having bus unlocked)
|
||||
* returns false on error
|
||||
*/
|
||||
|
||||
static bool
|
||||
service_device(ide_device_info *device)
|
||||
{
|
||||
ide_qrequest *qrequest;
|
||||
int tag;
|
||||
|
||||
SHOW_FLOW0( 3, "Start servicing" );
|
||||
|
||||
// delete timeout first
|
||||
|
||||
// we must unlock bus before cancelling timer: if the timeout has
|
||||
// just been fired we have to wait for it, but in turn it waits
|
||||
// for the ide bus -> deadlock
|
||||
IDE_UNLOCK(device->bus);
|
||||
|
||||
cancel_timer(&device->reconnect_timer.te);
|
||||
|
||||
// between IDE_UNLOCK and cancel_timer the request may got
|
||||
// discarded due to timeout, so it's not a hardware problem
|
||||
// if servicing fails
|
||||
|
||||
// further, the device discards the entire queue if anything goes
|
||||
// wrong, thus we call send_abort_queue on each error
|
||||
// (we could also discard the queue without telling the device,
|
||||
// but we prefer setting the device into a safe state)
|
||||
|
||||
// ask device to continue
|
||||
if (!device_start_service(device, &tag)) {
|
||||
send_abort_queue(device);
|
||||
goto err;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "device starts service" );
|
||||
|
||||
// get tag of request
|
||||
qrequest = tag2request(device, tag);
|
||||
if (qrequest == NULL) {
|
||||
send_abort_queue(device);
|
||||
goto err;
|
||||
}
|
||||
|
||||
SHOW_FLOW( 3, "continue request %p with tag %d", qrequest, tag );
|
||||
|
||||
device->bus->active_qrequest = qrequest;
|
||||
|
||||
// from here on, queuing is ATA read/write specific, so you have to
|
||||
// modify that if you want to support ATAPI queuing!
|
||||
if (check_rw_error(device, qrequest)) {
|
||||
// if a read/write error occured, the request really failed
|
||||
finish_reset_queue(qrequest);
|
||||
goto err;
|
||||
}
|
||||
|
||||
// all ATA commands continue with a DMA request
|
||||
if (!prepare_dma(device, qrequest)) {
|
||||
// this is effectively impossible: before the command was initially
|
||||
// sent, prepare_dma had been called and obviously didn't fail,
|
||||
// so why should it fail now?
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
finish_reset_queue(qrequest);
|
||||
goto err;
|
||||
}
|
||||
|
||||
SHOW_FLOW0( 3, "launch DMA" );
|
||||
|
||||
start_dma_wait_no_lock(device, qrequest);
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
// don't start timeout - all requests have been discarded at this point
|
||||
IDE_LOCK(device->bus);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** check if some device on bus wants to continue queued requests;
|
||||
*
|
||||
* (expects locked bus and bus in "accessing" state)
|
||||
* returns true if servicing a command (implies having bus unlocked)
|
||||
* returns false if nothing to service
|
||||
*/
|
||||
|
||||
bool
|
||||
try_service(ide_device_info *device)
|
||||
{
|
||||
bool this_device_needs_service;
|
||||
ide_device_info *other_device;
|
||||
|
||||
other_device = device->other_device;
|
||||
|
||||
// first check whether current device requests service
|
||||
// (the current device is selected anyway, so asking it is fast)
|
||||
this_device_needs_service = check_service_req(device);
|
||||
|
||||
// service other device first as it was certainly waiting
|
||||
// longer then the current device
|
||||
if (other_device != device && check_service_req(other_device)) {
|
||||
if (service_device(other_device)) {
|
||||
// we handed over control; start timeout for device
|
||||
// (see below about fairness)
|
||||
if (device->num_running_reqs > 0) {
|
||||
if (!device->reconnect_timer_installed) {
|
||||
device->reconnect_timer_installed = true;
|
||||
add_timer(&device->reconnect_timer.te, reconnect_timeout,
|
||||
IDE_RELEASE_TIMEOUT, B_ONE_SHOT_RELATIVE_TIMER);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// service our device second
|
||||
if (this_device_needs_service) {
|
||||
if (service_device(device))
|
||||
return true;
|
||||
}
|
||||
|
||||
// if device has pending reqs, start timeout.
|
||||
// this may sound strange as we cannot be blamed if the
|
||||
// other device blocks us. But: the timeout is delayed until
|
||||
// the bus is idle, so once the other device finishes its
|
||||
// access, we have a chance of servicing all the pending
|
||||
// commands before the timeout handler is executed
|
||||
if (device->num_running_reqs > 0) {
|
||||
if (!device->reconnect_timer_installed) {
|
||||
device->reconnect_timer_installed = true;
|
||||
add_timer(&device->reconnect_timer.te, reconnect_timeout,
|
||||
IDE_RELEASE_TIMEOUT, B_ONE_SHOT_RELATIVE_TIMER);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
initialize_qreq_array(ide_device_info *device, int queue_depth)
|
||||
{
|
||||
int i;
|
||||
|
||||
device->queue_depth = queue_depth;
|
||||
|
||||
SHOW_FLOW( 3, "queue depth=%d", device->queue_depth );
|
||||
|
||||
device->qreq_array = (ide_qrequest *)malloc(queue_depth * sizeof(ide_qrequest));
|
||||
if (device->qreq_array == NULL)
|
||||
return false;
|
||||
|
||||
memset(device->qreq_array, 0, queue_depth * sizeof(ide_qrequest));
|
||||
|
||||
device->free_qrequests = NULL;
|
||||
|
||||
for (i = queue_depth - 1; i >= 0 ; --i) {
|
||||
ide_qrequest *qrequest = &device->qreq_array[i];
|
||||
|
||||
qrequest->next = device->free_qrequests;
|
||||
device->free_qrequests = qrequest;
|
||||
|
||||
qrequest->running = false;
|
||||
qrequest->device = device;
|
||||
qrequest->tag = i;
|
||||
qrequest->request = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
destroy_qreq_array(ide_device_info *device)
|
||||
{
|
||||
if (device->qreq_array) {
|
||||
free(device->qreq_array);
|
||||
device->qreq_array = NULL;
|
||||
}
|
||||
|
||||
device->num_running_reqs = 0;
|
||||
device->queue_depth = 0;
|
||||
device->free_qrequests = NULL;
|
||||
}
|
||||
|
||||
|
||||
/** change maximum number of queuable requests */
|
||||
|
||||
static bool
|
||||
change_qreq_array(ide_device_info *device, int queue_depth)
|
||||
{
|
||||
ide_qrequest *qreq_array = device->qreq_array;
|
||||
ide_qrequest *old_free_qrequests = device->free_qrequests;
|
||||
int old_queue_depth = queue_depth;
|
||||
|
||||
// be very causious - even if no queuing supported, we still need
|
||||
// one queue entry; if this allocation fails, we have a device that
|
||||
// cannot accept any command, which would be odd
|
||||
if (initialize_qreq_array( device, queue_depth)) {
|
||||
free(qreq_array);
|
||||
return true;
|
||||
}
|
||||
|
||||
device->qreq_array = qreq_array;
|
||||
device->num_running_reqs = 0;
|
||||
device->queue_depth = old_queue_depth;
|
||||
device->free_qrequests = old_free_qrequests;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** reconnect timeout worker
|
||||
* must be called as a synced procedure call, i.e.
|
||||
* the bus is allocated for us
|
||||
*/
|
||||
|
||||
void
|
||||
reconnect_timeout_worker(ide_bus_info *bus, void *arg)
|
||||
{
|
||||
ide_device_info *device = (ide_device_info *)arg;
|
||||
|
||||
// perhaps all requests have been successfully finished
|
||||
// when the synced pc was waiting; in this case, everything's fine
|
||||
// (this is _very_ important if the other device blocks the bus
|
||||
// for a long time - if this leads to a reconnect timeout, the
|
||||
// device has a last chance by servicing all requests without
|
||||
// delay, in which case this function gets delayed until all
|
||||
// pending requests are finished and the following test would
|
||||
// make sure that this false alarm gets ignored)
|
||||
if (device->num_running_reqs > 0) {
|
||||
// if one queued command fails, all of them fail
|
||||
send_abort_queue(device);
|
||||
|
||||
// if too many timeouts occure, disable CQ
|
||||
if (++device->CQ_failures > MAX_CQ_FAILURES) {
|
||||
device->CQ_enabled = false;
|
||||
|
||||
change_qreq_array(device, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// we've blocked the bus in dpc - undo that
|
||||
scsi->unblock_bus(device->bus->scsi_cookie);
|
||||
}
|
||||
|
||||
|
||||
/** dpc callback for reconnect timeout */
|
||||
|
||||
static void
|
||||
reconnect_timeout_dpc(void *arg)
|
||||
{
|
||||
ide_device_info *device = (ide_device_info *)arg;
|
||||
|
||||
// even though we are in the service thread,
|
||||
// the bus can be in use (e.g. by an ongoing PIO command),
|
||||
// so we have to issue a synced procedure call which
|
||||
// waits for the command to be finished
|
||||
|
||||
// meanwhile, we don't want any command to be issued to this device
|
||||
// as we are going to discard the entire device queue;
|
||||
// sadly, we don't have a reliable XPT device handle, so we block
|
||||
// bus instead (as this is an error handler, so performance is
|
||||
// not crucial)
|
||||
scsi->block_bus(device->bus->scsi_cookie);
|
||||
schedule_synced_pc(device->bus, &device->reconnect_timeout_synced_pc, device);
|
||||
}
|
||||
|
||||
|
||||
/** timer function for reconnect timeout */
|
||||
|
||||
int32
|
||||
reconnect_timeout(timer *arg)
|
||||
{
|
||||
ide_device_info *device = ((ide_device_timer_info *)arg)->device;
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
// we are polite and let the service thread do the job
|
||||
scsi->schedule_dpc(bus->scsi_cookie, device->reconnect_timeout_dpc,
|
||||
reconnect_timeout_dpc, device);
|
||||
|
||||
return B_INVOKE_SCHEDULER;
|
||||
}
|
||||
|
||||
|
||||
/** tell device to abort all queued requests
|
||||
* (tells XPT to resubmit these requests)
|
||||
* return: true - abort successful
|
||||
* false - abort failed (in this case, nothing can be done)
|
||||
*/
|
||||
|
||||
bool
|
||||
send_abort_queue(ide_device_info *device)
|
||||
{
|
||||
int status;
|
||||
ide_bus_info *bus = device->bus;
|
||||
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
device->tf.write.command = IDE_CMD_NOP;
|
||||
// = discard outstanding commands
|
||||
device->tf.write.features = IDE_CMD_NOP_NOP;
|
||||
|
||||
device->tf_param_mask = ide_mask_features;
|
||||
|
||||
if (!send_command(device, NULL, true, 0, ide_state_accessing))
|
||||
goto err;
|
||||
|
||||
if (!wait_for_drdy(device))
|
||||
goto err;
|
||||
|
||||
// device must answer "command rejected" and discard outstanding commands
|
||||
status = bus->controller->get_altstatus(bus->channel);
|
||||
|
||||
if ((status & ide_status_err) == 0)
|
||||
goto err;
|
||||
|
||||
if (!bus->controller->read_command_block_regs(bus->channel,
|
||||
&device->tf, ide_mask_error)) {
|
||||
// don't bother trying bus_reset as controller disappeared
|
||||
device->subsys_status = SCSI_HBA_ERR;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((device->tf.read.error & ide_error_abrt) == 0)
|
||||
goto err;
|
||||
|
||||
finish_all_requests(device, NULL, 0, true);
|
||||
return true;
|
||||
|
||||
err:
|
||||
// ouch! device didn't react - we have to reset it
|
||||
return reset_device(device, NULL);
|
||||
}
|
532
src/add-ons/kernel/bus_managers/ide/scsi2ata.c
Normal file
532
src/add-ons/kernel/bus_managers/ide/scsi2ata.c
Normal file
@ -0,0 +1,532 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Converts SCSI commands to ATA commands.
|
||||
*/
|
||||
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
#include "ide_cmds.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** emulate MODE SENSE 10 command */
|
||||
|
||||
static void
|
||||
ata_mode_sense_10(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
scsi_cmd_mode_sense_10 *cmd = (scsi_cmd_mode_sense_10 *)request->cdb;
|
||||
scsi_mode_param_header_10 param_header;
|
||||
scsi_modepage_contr contr;
|
||||
scsi_mode_param_block_desc block_desc;
|
||||
size_t total_length =
|
||||
sizeof(scsi_mode_param_header_10) +
|
||||
sizeof(scsi_mode_param_block_desc) +
|
||||
sizeof(scsi_modepage_contr);
|
||||
scsi_mode_param_dev_spec_da devspec = {
|
||||
res0_0 : 0,
|
||||
DPOFUA : 0,
|
||||
res0_6 : 0,
|
||||
WP : 0
|
||||
};
|
||||
int allocation_length;
|
||||
|
||||
SHOW_ERROR0( 0, "Hi!" );
|
||||
|
||||
allocation_length = ((int16)cmd->high_allocation_length << 8)
|
||||
| cmd->low_allocation_length;
|
||||
|
||||
// we answer control page requests and "all pages" requests
|
||||
// (as the latter are the same as the first)
|
||||
if( (cmd->page_code != SCSI_MODEPAGE_CONTROL &&
|
||||
cmd->page_code != SCSI_MODEPAGE_ALL) ||
|
||||
(cmd->PC != SCSI_MODE_SENSE_PC_CURRENT &&
|
||||
cmd->PC != SCSI_MODE_SENSE_PC_SAVED))
|
||||
{
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD );
|
||||
return;
|
||||
}
|
||||
|
||||
//param_header = (scsi_mode_param_header_10 *)request->data;
|
||||
param_header.low_mode_data_len = total_length - 1;
|
||||
param_header.high_mode_data_len = 0;
|
||||
param_header.medium_type = 0; // XXX standard is a bit vague here
|
||||
param_header.dev_spec_parameter = *(uint8 *)&devspec;
|
||||
param_header.low_block_desc_len = sizeof( scsi_mode_param_block_desc );
|
||||
param_header.high_block_desc_len = 0;
|
||||
|
||||
copy_sg_data( request, 0, allocation_length,
|
||||
¶m_header, sizeof( param_header ), false );
|
||||
|
||||
/*block_desc = (scsi_mode_param_block_desc *)(request->data
|
||||
+ sizeof( *param_header ));*/
|
||||
memset( &block_desc, 0, sizeof( block_desc ));
|
||||
// density is reserved (0), descriptor apply to entire medium (num_blocks=0)
|
||||
// remains the blocklen to be set
|
||||
block_desc.high_blocklen = 0;
|
||||
block_desc.med_blocklen = 512 >> 8;
|
||||
block_desc.low_blocklen = 512 & 0xff;
|
||||
|
||||
copy_sg_data( request, sizeof( param_header ), allocation_length,
|
||||
&block_desc, sizeof( block_desc ), false );
|
||||
|
||||
/*contr = (scsi_modepage_contr *)(request->data
|
||||
+ sizeof( *param_header )
|
||||
+ ((uint16)param_header->high_block_desc_len << 8)
|
||||
+ param_header->low_block_desc_len);*/
|
||||
|
||||
memset( &contr, 0, sizeof( contr ));
|
||||
contr.RLEC = false;
|
||||
contr.DQue = !device->CQ_enabled;
|
||||
contr.QErr = false; // when a command fails we requeue all
|
||||
// lost commands automagically
|
||||
contr.QAM = SCSI_QAM_UNRESTRICTED;
|
||||
|
||||
copy_sg_data( request,
|
||||
sizeof( param_header )
|
||||
+ ((uint16)param_header.high_block_desc_len << 8)
|
||||
+ param_header.low_block_desc_len,
|
||||
allocation_length,
|
||||
&contr, sizeof( contr ), false );
|
||||
|
||||
// the number of bytes that were transferred to buffer is
|
||||
// restricted by allocation length and by request data buffer size
|
||||
total_length = min( total_length, allocation_length );
|
||||
total_length = min( total_length, request->data_len );
|
||||
|
||||
request->data_resid = request->data_len - total_length;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// emulate modifying control page
|
||||
static bool ata_mode_select_contrpage( ide_device_info *device, ide_qrequest *qrequest,
|
||||
scsi_modepage_contr *page )
|
||||
{
|
||||
if( page->header.page_length != sizeof( *page ) - sizeof( page->header )) {
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR );
|
||||
return false;
|
||||
}
|
||||
|
||||
// we only support enabling/disabling command queuing
|
||||
enable_CQ( device, !page->DQue );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// emulate MODE SELECT 10 command
|
||||
static void ata_mode_select_10( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
scsi_cmd_mode_select_10 *cmd = (scsi_cmd_mode_select_10 *)request->cdb;
|
||||
scsi_mode_param_header_10 param_header;
|
||||
scsi_modepage_header page_header;
|
||||
int total_length;
|
||||
uint modepage_offset;
|
||||
char modepage_buffer[64]; // !!! enlarge this to support longer mode pages
|
||||
|
||||
if( cmd->SP || cmd->PF != 1 ) {
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD );
|
||||
return;
|
||||
}
|
||||
|
||||
total_length = min( request->data_len,
|
||||
((uint16)cmd->high_param_list_length << 8) | cmd->low_param_list_length);
|
||||
|
||||
// first, retrieve page header to get size of different chunks
|
||||
//param_header = (scsi_mode_param_header_10 *)request->data;
|
||||
if( !copy_sg_data( request, 0, total_length,
|
||||
¶m_header, sizeof( param_header ), true ))
|
||||
goto err;
|
||||
|
||||
total_length = min( total_length,
|
||||
(((uint16)param_header.high_mode_data_len << 8) | param_header.low_mode_data_len) + 1 );
|
||||
|
||||
// this is the start of the first mode page;
|
||||
// we ignore the block descriptor silently
|
||||
modepage_offset =
|
||||
sizeof( param_header ) +
|
||||
(((uint16)param_header.high_block_desc_len << 8) | param_header.low_block_desc_len);
|
||||
|
||||
// go through list of pages
|
||||
while( modepage_offset < total_length ) {
|
||||
int page_len;
|
||||
|
||||
// get header to know how long page is
|
||||
if( !copy_sg_data( request, modepage_offset, total_length,
|
||||
&page_header, sizeof( page_header ), true ))
|
||||
goto err;
|
||||
|
||||
// get size of one page and copy it to buffer
|
||||
page_len = page_header.page_length + sizeof( scsi_modepage_header );
|
||||
|
||||
// the buffer has a maximum size - this is really standard compliant but
|
||||
// sufficient for our needs
|
||||
if( page_len > sizeof( modepage_buffer ))
|
||||
goto err;
|
||||
|
||||
if( !copy_sg_data( request, modepage_offset, total_length,
|
||||
&modepage_buffer, min( page_len, sizeof( modepage_buffer )), true ))
|
||||
goto err;
|
||||
|
||||
// modify page;
|
||||
// currently, we only support the control mode page
|
||||
switch( page_header.page_code ) {
|
||||
case SCSI_MODEPAGE_CONTROL:
|
||||
if( !ata_mode_select_contrpage( device, qrequest,
|
||||
(scsi_modepage_contr *)modepage_buffer ))
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
set_sense( device,
|
||||
SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_PARAM_LIST_FIELD );
|
||||
return;
|
||||
}
|
||||
|
||||
modepage_offset += page_len;
|
||||
}
|
||||
|
||||
if( modepage_offset != total_length )
|
||||
goto err;
|
||||
|
||||
request->data_resid = request->data_len - total_length;
|
||||
return;
|
||||
|
||||
// if we arrive here, data length was incorrect
|
||||
err:
|
||||
set_sense( device,
|
||||
SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR );
|
||||
}
|
||||
|
||||
|
||||
// emulate TEST UNIT READY
|
||||
static bool ata_tur( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
SHOW_FLOW0( 3, "" );
|
||||
|
||||
if( !device->infoblock.RMSN_supported ||
|
||||
device->infoblock._127_RMSN_support != 1 )
|
||||
return true;
|
||||
|
||||
// ask device about status
|
||||
device->tf_param_mask = 0;
|
||||
device->tf.write.command = IDE_CMD_GET_MEDIA_STATUS;
|
||||
|
||||
if( !send_command( device, qrequest, true, 15, ide_state_sync_waiting ))
|
||||
return false;
|
||||
|
||||
// bits ide_error_mcr | ide_error_mc | ide_error_wp are also valid
|
||||
// but not requested by TUR; ide_error_wp can safely be ignored, but
|
||||
// we don't want to loose media change (request) reports
|
||||
if( !check_output( device, true,
|
||||
ide_error_nm | ide_error_abrt | ide_error_mcr | ide_error_mc,
|
||||
false ))
|
||||
{
|
||||
// SCSI spec is unclear here: we shouldn't report "media change (request)"
|
||||
// but what to do if there is one? anyway - we report them
|
||||
;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// flush internal device cache
|
||||
static bool ata_flush_cache( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
// we should also ask for FLUSH CACHE support, but everyone denies it
|
||||
// (looks like they cheat to gain some performance advantage, but
|
||||
// that's pretty useless: everyone does it...)
|
||||
if( !device->infoblock.write_cache_supported )
|
||||
return true;
|
||||
|
||||
device->tf_param_mask = 0;
|
||||
device->tf.lba.command = device->use_48bits ? IDE_CMD_FLUSH_CACHE_EXT
|
||||
: IDE_CMD_FLUSH_CACHE;
|
||||
|
||||
// spec says that this may take more then 30s, how much more?
|
||||
if( !send_command( device, qrequest, true, 60, ide_state_sync_waiting ))
|
||||
return false;
|
||||
|
||||
wait_for_sync( device->bus );
|
||||
|
||||
return check_output( device, true, ide_error_abrt, false );
|
||||
}
|
||||
|
||||
|
||||
// load or eject medium
|
||||
// load = true - load medium
|
||||
static bool ata_load_eject( ide_device_info *device, ide_qrequest *qrequest, bool load )
|
||||
{
|
||||
if( load ) {
|
||||
// ATA doesn't support loading
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_NOT_SUPPORTED );
|
||||
return false;
|
||||
}
|
||||
|
||||
device->tf_param_mask = 0;
|
||||
device->tf.lba.command = IDE_CMD_MEDIA_EJECT;
|
||||
|
||||
if( !send_command( device, qrequest, true, 15, ide_state_sync_waiting ))
|
||||
return false;
|
||||
|
||||
wait_for_sync( device->bus );
|
||||
|
||||
return check_output( device, true, ide_error_abrt | ide_error_nm, false );
|
||||
}
|
||||
|
||||
|
||||
// emulate PREVENT ALLOW command
|
||||
static bool ata_prevent_allow( ide_device_info *device, bool prevent )
|
||||
{
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_ILL_FUNCTION );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// emulate INQUIRY command
|
||||
static void ata_inquiry( ide_device_info *device, ide_qrequest *qrequest )
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
scsi_res_inquiry data;
|
||||
scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)request->cdb;
|
||||
uint allocation_length = cmd->allocation_length;
|
||||
int transfer_size;
|
||||
|
||||
if (cmd->EVPD || cmd->page_code) {
|
||||
set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
|
||||
return;
|
||||
}
|
||||
|
||||
memset( &data, 0, sizeof( data ));
|
||||
|
||||
data.device_type = scsi_dev_direct_access;
|
||||
data.device_qualifier = scsi_periph_qual_connected;
|
||||
|
||||
data.device_type_modifier = 0;
|
||||
data.RMB = false;
|
||||
|
||||
data.ANSI_version = 2;
|
||||
data.ECMA_version = 0;
|
||||
data.ISO_version = 0;
|
||||
|
||||
data.response_data_format = 2;
|
||||
data.TrmIOP = false; // to be changed if we support TERM I/O
|
||||
|
||||
data.additional_length = sizeof( scsi_res_inquiry ) - 4;
|
||||
|
||||
data.SftRe = false;
|
||||
data.CmdQue = device->queue_depth > 1;
|
||||
data.Linked = false;
|
||||
|
||||
// these values are free-style
|
||||
data.Sync = false;
|
||||
data.WBus16 = true;
|
||||
data.WBus32 = false;
|
||||
|
||||
data.RelAdr = false;
|
||||
|
||||
// the following fields are *much* to small, sigh...
|
||||
memcpy( data.vendor_ident, device->infoblock.model_number,
|
||||
sizeof( data.vendor_ident ));
|
||||
memcpy( data.product_ident, device->infoblock.model_number + 8,
|
||||
sizeof( data.product_ident ));
|
||||
memcpy( data.product_rev, " ", sizeof( data.product_rev ));
|
||||
|
||||
copy_sg_data( request, 0, allocation_length, &data, sizeof( data ), false );
|
||||
|
||||
transfer_size = min( sizeof( data ), allocation_length );
|
||||
transfer_size = min( transfer_size, request->data_len );
|
||||
|
||||
request->data_resid = request->data_len - transfer_size;
|
||||
}
|
||||
|
||||
|
||||
/** emulate READ CAPACITY command */
|
||||
|
||||
static void
|
||||
read_capacity(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
scsi_res_read_capacity data;
|
||||
scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb;
|
||||
uint32 last_block;
|
||||
|
||||
if (cmd->PMI || cmd->top_LBA || cmd->high_LBA || cmd->mid_LBA || cmd->low_LBA) {
|
||||
set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
|
||||
return;
|
||||
}
|
||||
|
||||
// ToDo: 512 bytes fixed block size?
|
||||
data.top_block_size = (512 >> 24) & 0xff;
|
||||
data.high_block_size = (512 >> 16) & 0xff;
|
||||
data.mid_block_size = (512 >> 8) & 0xff;
|
||||
data.low_block_size = 512 & 0xff;
|
||||
|
||||
last_block = device->total_sectors - 1;
|
||||
|
||||
data.top_LBA = (last_block >> 24) & 0xff;
|
||||
data.high_LBA = (last_block >> 16) & 0xff;
|
||||
data.mid_LBA = (last_block >> 8) & 0xff;
|
||||
data.low_LBA = last_block & 0xff;
|
||||
|
||||
copy_sg_data(request, 0, request->data_len, &data, sizeof(data), false);
|
||||
|
||||
request->data_resid = max(request->data_len - sizeof(data), 0);
|
||||
}
|
||||
|
||||
|
||||
/** execute SCSI command */
|
||||
|
||||
void
|
||||
ata_exec_io(ide_device_info *device, ide_qrequest *qrequest)
|
||||
{
|
||||
scsi_ccb *request = qrequest->request;
|
||||
|
||||
SHOW_FLOW( 3, "command=%x", request->cdb[0] );
|
||||
|
||||
// ATA devices have one LUN only
|
||||
if( request->target_lun != 0 ) {
|
||||
request->subsys_status = SCSI_SEL_TIMEOUT;
|
||||
finish_request( qrequest, false );
|
||||
return;
|
||||
}
|
||||
|
||||
// starting a request means deleting sense, so don't do it if
|
||||
// the command wants to read it
|
||||
if( request->cdb[0] != SCSI_OP_REQUEST_SENSE )
|
||||
start_request( device, qrequest );
|
||||
|
||||
switch( request->cdb[0] ) {
|
||||
case SCSI_OP_TUR:
|
||||
ata_tur( device, qrequest );
|
||||
break;
|
||||
|
||||
case SCSI_OP_REQUEST_SENSE:
|
||||
ide_request_sense( device, qrequest );
|
||||
return;
|
||||
|
||||
case SCSI_OP_FORMAT: /* FORMAT UNIT */
|
||||
// we could forward request to disk, but modern disks cannot
|
||||
// be formatted anyway, so we just refuse request
|
||||
// (exceptions are removable media devices, but to my knowledge
|
||||
// they don't have to be formatted as well)
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE );
|
||||
break;
|
||||
|
||||
case SCSI_OP_INQUIRY:
|
||||
ata_inquiry( device, qrequest );
|
||||
break;
|
||||
|
||||
case SCSI_OP_MODE_SELECT_10:
|
||||
ata_mode_select_10( device, qrequest );
|
||||
break;
|
||||
|
||||
case SCSI_OP_MODE_SENSE_10:
|
||||
ata_mode_sense_10( device, qrequest );
|
||||
break;
|
||||
|
||||
case SCSI_OP_MODE_SELECT_6:
|
||||
case SCSI_OP_MODE_SENSE_6:
|
||||
// we've told SCSI bus manager to emulates these commands
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE );
|
||||
break;
|
||||
|
||||
case SCSI_OP_RESERVE:
|
||||
case SCSI_OP_RELEASE:
|
||||
// though mandatory, this doesn't make much sense in a
|
||||
// single initiator environment; so what
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE );
|
||||
break;
|
||||
|
||||
case SCSI_OP_START_STOP: {
|
||||
scsi_cmd_ssu *cmd = (scsi_cmd_ssu *)request->cdb;
|
||||
|
||||
// with no LoEj bit set, we should only allow/deny further access
|
||||
// we ignore that (unsupported for ATA)
|
||||
// with LoEj bit set, we should additionally either load or eject the medium
|
||||
// (start = 0 - eject; start = 1 - load)
|
||||
|
||||
if( !cmd->start )
|
||||
// we must always flush cache if start = 0
|
||||
ata_flush_cache( device, qrequest );
|
||||
|
||||
if( cmd->LoEj )
|
||||
ata_load_eject( device, qrequest, cmd->start );
|
||||
|
||||
break; }
|
||||
|
||||
case SCSI_OP_PREVENT_ALLOW: {
|
||||
scsi_cmd_prevent_allow *cmd = (scsi_cmd_prevent_allow *)request->cdb;
|
||||
|
||||
ata_prevent_allow( device, cmd->prevent );
|
||||
break; }
|
||||
|
||||
case SCSI_OP_READ_CAPACITY:
|
||||
read_capacity( device, qrequest );
|
||||
break;
|
||||
|
||||
case SCSI_OP_VERIFY:
|
||||
// does anyone uses this function?
|
||||
// effectly, it does a read-and-compare, which IDE doesn't support
|
||||
set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE );
|
||||
break;
|
||||
|
||||
case SCSI_OP_SYNCHRONIZE_CACHE:
|
||||
// we ignore range and immediate bit, we always immediately flush everything
|
||||
ata_flush_cache( device, qrequest );
|
||||
break;
|
||||
|
||||
// sadly, there are two possible read/write operation codes;
|
||||
// at least, the third one, read/write(12), is not valid for DAS
|
||||
case SCSI_OP_READ_6:
|
||||
case SCSI_OP_WRITE_6: {
|
||||
scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
|
||||
uint32 pos;
|
||||
size_t length;
|
||||
|
||||
pos = ((uint32)cmd->high_LBA << 16) | ((uint32)cmd->mid_LBA << 8)
|
||||
| (uint32)cmd->low_LBA;
|
||||
|
||||
length = cmd->length != 0 ? cmd->length : 256;
|
||||
|
||||
SHOW_FLOW( 3, "READ6/WRITE6 pos=%ux, length=%ux", (uint)pos, (uint)length );
|
||||
|
||||
ata_send_rw( device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_6 );
|
||||
return; }
|
||||
|
||||
case SCSI_OP_READ_10:
|
||||
case SCSI_OP_WRITE_10: {
|
||||
scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
|
||||
uint32 pos;
|
||||
size_t length;
|
||||
|
||||
pos = ((uint32)cmd->top_LBA << 24) | ((uint32)cmd->high_LBA << 16)
|
||||
| ((uint32)cmd->mid_LBA << 8) | (uint32)cmd->low_LBA;
|
||||
|
||||
length = ((uint32)cmd->high_length << 8) | cmd->low_length;
|
||||
|
||||
if( length != 0 ) {
|
||||
ata_send_rw( device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_10 );
|
||||
} else {
|
||||
// we cannot transfer zero blocks (apart from LBA48)
|
||||
finish_request( qrequest, false );
|
||||
}
|
||||
return; }
|
||||
|
||||
default:
|
||||
set_sense( device,
|
||||
SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE );
|
||||
}
|
||||
|
||||
finish_checksense( qrequest );
|
||||
}
|
474
src/add-ons/kernel/bus_managers/ide/sync.c
Normal file
474
src/add-ons/kernel/bus_managers/ide/sync.c
Normal file
@ -0,0 +1,474 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Part of Open IDE bus manager
|
||||
|
||||
Handling of passive waiting and synchronized procedure calls.
|
||||
The latter are calls that get delayed until the bus is idle.
|
||||
*/
|
||||
|
||||
|
||||
#include "ide_internal.h"
|
||||
#include "ide_sim.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRACE_SYNC
|
||||
#ifdef TRACE_SYNC
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/** DPC handler for IRQs */
|
||||
|
||||
void
|
||||
ide_dpc(void *arg)
|
||||
{
|
||||
ide_bus_info *bus = (ide_bus_info *)arg;
|
||||
ide_qrequest *qrequest;
|
||||
ide_device_info *device;
|
||||
|
||||
TRACE(("ide_dpc()\n"));
|
||||
|
||||
//snooze(500000);
|
||||
|
||||
// IRQ handler doesn't tell us whether this bus was in async_wait or
|
||||
// in idle state, so we just check whether there is an active request,
|
||||
// which means that we were async_waiting
|
||||
if (bus->active_qrequest != NULL) {
|
||||
FAST_LOG1( bus->log, ev_ide_dpc_continue, (uint32)bus->active_qrequest );
|
||||
TRACE(("continue command\n"));
|
||||
|
||||
// cancel timeout
|
||||
cancel_timer(&bus->timer.te);
|
||||
|
||||
qrequest = bus->active_qrequest;
|
||||
device = qrequest->device;
|
||||
|
||||
// not perfect but simple: we simply know who is waiting why
|
||||
if (device->is_atapi)
|
||||
packet_dpc(qrequest);
|
||||
else {
|
||||
if (qrequest->uses_dma)
|
||||
ata_dpc_DMA(qrequest);
|
||||
else
|
||||
ata_dpc_PIO(qrequest);
|
||||
}
|
||||
} else {
|
||||
// no request active, so this must be a service request or
|
||||
// a spurious IRQ; access_finished will take care of testing
|
||||
// for service requests
|
||||
TRACE(("irq in idle mode - possible service request\n"));
|
||||
|
||||
FAST_LOG0(bus->log, ev_ide_dpc_service);
|
||||
|
||||
device = get_current_device(bus);
|
||||
|
||||
if (device == NULL) {
|
||||
// got an interrupt from a non-existing device
|
||||
// either this is a spurious interrupt or there *is* a device
|
||||
// but we haven't detected it - we better ignore it silently
|
||||
access_finished(bus, bus->first_device);
|
||||
} else {
|
||||
// access_finished always checks the other device first, but as
|
||||
// we do have a service request, we negate the negation
|
||||
access_finished(bus, device->other_device);
|
||||
}
|
||||
|
||||
// let XPT resend commands that got blocked
|
||||
scsi->cont_send_bus(bus->scsi_cookie);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
/*err:
|
||||
xpt->cont_send( bus->xpt_cookie );*/
|
||||
}
|
||||
|
||||
|
||||
/** handler for IDE IRQs */
|
||||
|
||||
status_t
|
||||
ide_irq_handler(ide_bus_info *bus, uint8 status)
|
||||
{
|
||||
ide_device_info *device;
|
||||
|
||||
TRACE(("ide_irq_handler()\n"));
|
||||
FAST_LOG0(bus->log, ev_ide_irq_handle);
|
||||
|
||||
// we need to lock bus to have a solid bus state
|
||||
// (side effect: we lock out the timeout handler and get
|
||||
// delayed if the IRQ happens at the same time as a command is
|
||||
// issued; in the latter case, we have no official way to determine
|
||||
// whether the command was issued before or afterwards; if it was
|
||||
// afterwards, the device must not be busy; if it was before,
|
||||
// the device is either busy because of the sent command, or it's
|
||||
// not busy as the command has already been finished, i.e. there
|
||||
// was a second IRQ which we've overlooked as we didn't acknowledge
|
||||
// the first IRQ)
|
||||
IDE_LOCK(bus);
|
||||
|
||||
device = bus->active_device;
|
||||
|
||||
if (device == NULL) {
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
TRACE(("IRQ though there is no active device\n"));
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
}
|
||||
|
||||
if ((status & ide_status_bsy) != 0) {
|
||||
// the IRQ seems to be fired before the last command was sent,
|
||||
// i.e. it's not the one that signals finishing of command
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
TRACE(("IRQ though device is busy\n"));
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
}
|
||||
|
||||
TRACE(("state: %d\n", bus->state));
|
||||
|
||||
switch (bus->state) {
|
||||
case ide_state_async_waiting:
|
||||
TRACE(("async waiting\n"));
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
scsi->schedule_dpc(bus->scsi_cookie, bus->irq_dpc, ide_dpc, bus);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
|
||||
case ide_state_idle:
|
||||
TRACE(("idle\n"));
|
||||
|
||||
// this must be a service request;
|
||||
// if no request is pending, the IRQ was fired wrongly
|
||||
if (bus->num_running_reqs == 0) {
|
||||
IDE_UNLOCK(bus);
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
scsi->schedule_dpc(bus->scsi_cookie, bus->irq_dpc, ide_dpc, bus);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
|
||||
case ide_state_sync_waiting:
|
||||
TRACE(("sync waiting\n"));
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
bus->sync_wait_timeout = false;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
release_sem_etc(bus->sync_wait_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
|
||||
case ide_state_accessing:
|
||||
TRACE(("spurious IRQ - there is a command being executed\n"));
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
|
||||
default:
|
||||
dprintf("BUG: unknown state (%d)\n", bus->state);
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** cancel IRQ timeout
|
||||
* it doesn't matter whether there really was a timout running;
|
||||
* on return, bus state is set to _accessing_
|
||||
*/
|
||||
|
||||
void
|
||||
cancel_irq_timeout(ide_bus_info *bus)
|
||||
{
|
||||
FAST_LOG0(bus->log, ev_ide_cancel_irq_timeout);
|
||||
|
||||
IDE_LOCK(bus);
|
||||
bus->state = ide_state_accessing;
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
cancel_timer(&bus->timer.te);
|
||||
}
|
||||
|
||||
|
||||
/** start waiting for IRQ with bus lock hold
|
||||
* new_state must be either sync_wait or async_wait
|
||||
*/
|
||||
|
||||
void
|
||||
start_waiting(ide_bus_info *bus, uint32 timeout, int new_state)
|
||||
{
|
||||
int res;
|
||||
|
||||
FAST_LOG1(bus->log, ev_ide_start_waiting, new_state);
|
||||
|
||||
TRACE(("timeout = %u\n", (uint)timeout));
|
||||
|
||||
bus->state = new_state;
|
||||
|
||||
res = add_timer(&bus->timer.te, ide_timeout,
|
||||
(bigtime_t)timeout * 1000000, B_ONE_SHOT_RELATIVE_TIMER);
|
||||
|
||||
if (res != B_OK)
|
||||
panic("Error setting timeout (%s)", strerror(res));
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
}
|
||||
|
||||
|
||||
/** start waiting for IRQ with bus lock not hold */
|
||||
|
||||
void
|
||||
start_waiting_nolock(ide_bus_info *bus, uint32 timeout, int new_state)
|
||||
{
|
||||
IDE_LOCK(bus);
|
||||
start_waiting(bus, timeout, new_state);
|
||||
}
|
||||
|
||||
|
||||
/** wait for sync IRQ */
|
||||
|
||||
void
|
||||
wait_for_sync(ide_bus_info *bus)
|
||||
{
|
||||
acquire_sem(bus->sync_wait_sem);
|
||||
cancel_timer(&bus->timer.te);
|
||||
}
|
||||
|
||||
|
||||
/** timeout dpc handler */
|
||||
|
||||
static void
|
||||
ide_timeout_dpc(void *arg)
|
||||
{
|
||||
ide_bus_info *bus = (ide_bus_info *)arg;
|
||||
ide_qrequest *qrequest;
|
||||
ide_device_info *device;
|
||||
|
||||
qrequest = bus->active_qrequest;
|
||||
device = qrequest->device;
|
||||
|
||||
FAST_LOG1(bus->log, ev_ide_timeout_dpc, (uint32)qrequest);
|
||||
|
||||
// this also resets overlapped commands
|
||||
reset_device(device, qrequest);
|
||||
|
||||
device->subsys_status = SCSI_CMD_TIMEOUT;
|
||||
|
||||
if (qrequest->uses_dma) {
|
||||
if (++device->DMA_failures >= MAX_DMA_FAILURES) {
|
||||
dprintf("Disabling DMA because of too many errors\n");
|
||||
|
||||
device->DMA_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// let upper layer do the retry
|
||||
finish_checksense(qrequest);
|
||||
}
|
||||
|
||||
|
||||
/** timeout handler, called by system timer */
|
||||
|
||||
status_t
|
||||
ide_timeout(timer *arg)
|
||||
{
|
||||
ide_bus_info *bus = ((ide_bus_timer_info *)arg)->bus;
|
||||
|
||||
FAST_LOG0(bus->log, ev_ide_timeout);
|
||||
|
||||
TRACE(("ide_timeout(): %p\n", bus));
|
||||
|
||||
// we need to lock bus to have a solid bus state
|
||||
// (side effect: we lock out the IRQ handler)
|
||||
IDE_LOCK(bus);
|
||||
|
||||
switch (bus->state) {
|
||||
case ide_state_async_waiting:
|
||||
TRACE(("async waiting\n"));
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
scsi->schedule_dpc(bus->scsi_cookie, bus->irq_dpc, ide_timeout_dpc, bus);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
|
||||
case ide_state_sync_waiting:
|
||||
TRACE(("sync waiting\n"));
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
bus->sync_wait_timeout = true;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
release_sem_etc(bus->sync_wait_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
return B_INVOKE_SCHEDULER;
|
||||
|
||||
case ide_state_accessing:
|
||||
TRACE(("came too late - IRQ occured already\n"));
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
|
||||
default:
|
||||
// this case also happens if a timeout fires too late;
|
||||
// unless there is a bug, the timeout should always be canceled
|
||||
// before declaring bus as being idle
|
||||
dprintf("BUG: unknown state (%d)\n", (int)bus->state);
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
return B_DO_NOT_RESCHEDULE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_synced_pc(ide_synced_pc *pc, ide_synced_pc_func func)
|
||||
{
|
||||
pc->func = func;
|
||||
pc->registered = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
uninit_synced_pc(ide_synced_pc *pc)
|
||||
{
|
||||
if (pc->registered)
|
||||
panic("Tried to clean up pending synced PC\n");
|
||||
}
|
||||
|
||||
|
||||
/** schedule a synced pc
|
||||
* a synced pc gets executed as soon as the bus becomes idle
|
||||
*/
|
||||
|
||||
status_t
|
||||
schedule_synced_pc(ide_bus_info *bus, ide_synced_pc *pc, void *arg)
|
||||
{
|
||||
//TRACE(());
|
||||
|
||||
IDE_LOCK(bus);
|
||||
|
||||
if (pc->registered) {
|
||||
// spc cannot be registered twice
|
||||
TRACE(("already registered\n"));
|
||||
return B_ERROR;
|
||||
} else if( bus->state != ide_state_idle ) {
|
||||
// bus isn't idle - spc must be added to pending list
|
||||
TRACE(("adding to pending list\n"));
|
||||
|
||||
pc->next = bus->synced_pc_list;
|
||||
bus->synced_pc_list = pc;
|
||||
pc->arg = arg;
|
||||
pc->registered = true;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// we have luck - bus is idle, so grab it before
|
||||
// releasing the lock
|
||||
|
||||
TRACE(("exec immediately\n"));
|
||||
|
||||
bus->state = ide_state_accessing;
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
TRACE(("go\n"));
|
||||
pc->func(bus, arg);
|
||||
|
||||
TRACE(("finished\n"));
|
||||
access_finished(bus, bus->first_device);
|
||||
|
||||
// meanwhile, we may have rejected SCSI commands;
|
||||
// usually, the XPT resends them once a command
|
||||
// has finished, but in this case XPT doesn't know
|
||||
// about our "private" command, so we have to tell about
|
||||
// idle bus manually
|
||||
TRACE(("tell SCSI bus manager about idle bus\n"));
|
||||
scsi->cont_send_bus(bus->scsi_cookie);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** execute list of synced pcs */
|
||||
|
||||
static void
|
||||
exec_synced_pcs(ide_bus_info *bus, ide_synced_pc *pc_list)
|
||||
{
|
||||
ide_synced_pc *pc;
|
||||
|
||||
// noone removes items from pc_list, so we don't need lock
|
||||
// to access entries
|
||||
for (pc = pc_list; pc; pc = pc->next) {
|
||||
pc->func(bus, pc->arg);
|
||||
}
|
||||
|
||||
// need lock now as items can be added to pc_list again as soon
|
||||
// as <registered> is reset
|
||||
IDE_LOCK(bus);
|
||||
|
||||
for (pc = pc_list; pc; pc = pc->next) {
|
||||
pc->registered = false;
|
||||
}
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
}
|
||||
|
||||
|
||||
/** finish bus access;
|
||||
* check if any device wants to service pending commands + execute synced_pc
|
||||
*/
|
||||
|
||||
void
|
||||
access_finished(ide_bus_info *bus, ide_device_info *device)
|
||||
{
|
||||
TRACE(("bus = %p, device = %p\n", bus, device));
|
||||
|
||||
while (true) {
|
||||
ide_synced_pc *synced_pc_list;
|
||||
|
||||
IDE_LOCK(bus);
|
||||
|
||||
// normally, there is always an device; only exception is a
|
||||
// bus without devices, not sure whether this can really happen though
|
||||
if (device) {
|
||||
if (try_service(device))
|
||||
return;
|
||||
}
|
||||
|
||||
// noone wants it, so execute pending synced_pc
|
||||
if (bus->synced_pc_list == NULL) {
|
||||
bus->state = ide_state_idle;
|
||||
IDE_UNLOCK(bus);
|
||||
return;
|
||||
}
|
||||
|
||||
synced_pc_list = bus->synced_pc_list;
|
||||
bus->synced_pc_list = NULL;
|
||||
|
||||
IDE_UNLOCK(bus);
|
||||
|
||||
exec_synced_pcs(bus, synced_pc_list);
|
||||
|
||||
// executed synced_pc may have generated other sync_pc,
|
||||
// thus the loop
|
||||
}
|
||||
}
|
139
src/add-ons/kernel/bus_managers/ide/virtual_memory.c
Normal file
139
src/add-ons/kernel/bus_managers/ide/virtual_memory.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
*/
|
||||
|
||||
/*
|
||||
VM helper functions.
|
||||
|
||||
Important assumption: get_memory_map must combine adjacent
|
||||
physical pages, so contignous memory always leads to a S/G
|
||||
list of length one.
|
||||
*/
|
||||
|
||||
#include "KernelExport_ext.h"
|
||||
#include "wrapper.h"
|
||||
|
||||
#include <vm.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/** get sg list of iovec
|
||||
* TBD: this should be moved to somewhere in kernel
|
||||
*/
|
||||
|
||||
status_t
|
||||
get_iovec_memory_map(iovec *vec, size_t vec_count, size_t vec_offset, size_t len,
|
||||
physical_entry *map, size_t max_entries, size_t *num_entries, size_t *mapped_len)
|
||||
{
|
||||
size_t cur_idx;
|
||||
size_t left_len;
|
||||
|
||||
SHOW_FLOW(3, "vec_count=%lu, vec_offset=%lu, len=%lu, max_entries=%lu",
|
||||
vec_count, vec_offset, len, max_entries);
|
||||
|
||||
// skip iovec blocks if needed
|
||||
while (vec_count > 0 && vec_offset > vec->iov_len) {
|
||||
vec_offset -= vec->iov_len;
|
||||
--vec_count;
|
||||
++vec;
|
||||
}
|
||||
|
||||
for (left_len = len, cur_idx = 0; left_len > 0 && vec_count > 0 && cur_idx < max_entries;) {
|
||||
char *range_start;
|
||||
size_t range_len;
|
||||
status_t res;
|
||||
size_t cur_num_entries, cur_mapped_len;
|
||||
uint32 tmp_idx;
|
||||
|
||||
SHOW_FLOW( 3, "left_len=%d, vec_count=%d, cur_idx=%d",
|
||||
(int)left_len, (int)vec_count, (int)cur_idx );
|
||||
|
||||
// map one iovec
|
||||
range_start = (char *)vec->iov_base + vec_offset;
|
||||
range_len = min( vec->iov_len - vec_offset, left_len );
|
||||
|
||||
SHOW_FLOW( 3, "range_start=%x, range_len=%x",
|
||||
(int)range_start, (int)range_len );
|
||||
|
||||
vec_offset = 0;
|
||||
|
||||
if ((res = get_memory_map(range_start, range_len, &map[cur_idx],
|
||||
max_entries - cur_idx)) != B_OK) {
|
||||
// according to docu, no error is ever reported - argh!
|
||||
SHOW_ERROR(1, "invalid io_vec passed (%s)", strerror(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
// stupid: get_memory_map does neither tell how many sg blocks
|
||||
// are used nor whether there were enough sg blocks at all;
|
||||
// -> determine that manually
|
||||
cur_mapped_len = 0;
|
||||
cur_num_entries = 0;
|
||||
|
||||
for (tmp_idx = cur_idx; tmp_idx < max_entries; ++tmp_idx) {
|
||||
if (map[tmp_idx].size == 0)
|
||||
break;
|
||||
|
||||
cur_mapped_len += map[tmp_idx].size;
|
||||
++cur_num_entries;
|
||||
}
|
||||
|
||||
if (cur_mapped_len == 0) {
|
||||
panic("get_memory_map() returned empty list; left_len=%d, idx=%d/%d",
|
||||
(int)left_len, (int)cur_idx, (int)max_entries);
|
||||
SHOW_ERROR(2, "get_memory_map() returned empty list; left_len=%d, idx=%d/%d",
|
||||
(int)left_len, (int)cur_idx, (int)max_entries);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
SHOW_FLOW( 3, "cur_num_entries=%d, cur_mapped_len=%x",
|
||||
(int)cur_num_entries, (int)cur_mapped_len );
|
||||
|
||||
// try to combine with previous sg block
|
||||
if (cur_num_entries > 0 && cur_idx > 0
|
||||
&& map[cur_idx].address == (char *)map[cur_idx - 1].address + map[cur_idx - 1].size) {
|
||||
SHOW_FLOW0( 3, "combine with previous chunk" );
|
||||
map[cur_idx - 1].size += map[cur_idx].size;
|
||||
memcpy(&map[cur_idx], &map[cur_idx + 1], (cur_num_entries - 1) * sizeof(map[0]));
|
||||
--cur_num_entries;
|
||||
}
|
||||
|
||||
cur_idx += cur_num_entries;
|
||||
left_len -= cur_mapped_len;
|
||||
|
||||
// advance iovec if current one is described completely
|
||||
if (cur_mapped_len == range_len) {
|
||||
++vec;
|
||||
--vec_count;
|
||||
}
|
||||
}
|
||||
|
||||
*num_entries = cur_idx;
|
||||
*mapped_len = len - left_len;
|
||||
|
||||
SHOW_FLOW( 3, "num_entries=%d, mapped_len=%x",
|
||||
(int)*num_entries, (int)*mapped_len );
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** map main memory into virtual address space */
|
||||
|
||||
status_t
|
||||
map_mainmemory(addr_t physicalAddress, void **_virtualAddress)
|
||||
{
|
||||
return vm_get_physical_page(physicalAddress, (addr_t *)_virtualAddress, PHYSICAL_PAGE_CAN_WAIT);
|
||||
// ToDo: check if CAN_WAIT is correct
|
||||
}
|
||||
|
||||
|
||||
/** unmap main memory from virtual address space */
|
||||
|
||||
status_t
|
||||
unmap_mainmemory(void *virtualAddress)
|
||||
{
|
||||
return vm_put_physical_page((addr_t)virtualAddress);
|
||||
}
|
||||
|
89
src/add-ons/kernel/bus_managers/ide/wrapper.h
Normal file
89
src/add-ons/kernel/bus_managers/ide/wrapper.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef _WRAPPER_H
|
||||
#define _WRAPPER_H
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <lock.h>
|
||||
|
||||
|
||||
// benaphores
|
||||
|
||||
#define INIT_BEN(x, prefix) benaphore_init(x, prefix)
|
||||
#define DELETE_BEN(x) benaphore_destroy(x)
|
||||
#define ACQUIRE_BEN(x) benaphore_lock(x)
|
||||
#define RELEASE_BEN(x) benaphore_unlock(x)
|
||||
|
||||
// debug output
|
||||
|
||||
#ifdef DEBUG_WAIT_ON_MSG
|
||||
# define DEBUG_WAIT snooze( DEBUG_WAIT_ON_MSG );
|
||||
#else
|
||||
# define DEBUG_WAIT
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_WAIT_ON_ERROR
|
||||
# define DEBUG_WAIT_ERROR snooze( DEBUG_WAIT_ON_ERROR );
|
||||
#else
|
||||
# define DEBUG_WAIT_ERROR
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_MAX_LEVEL_FLOW
|
||||
# define DEBUG_MAX_LEVEL_FLOW 4
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_MAX_LEVEL_INFO
|
||||
# define DEBUG_MAX_LEVEL_INFO 4
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_MAX_LEVEL_ERROR
|
||||
# define DEBUG_MAX_LEVEL_ERROR 4
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_MSG_PREFIX
|
||||
# define DEBUG_MSG_PREFIX ""
|
||||
#endif
|
||||
|
||||
#ifndef debug_level_flow
|
||||
# define debug_level_flow 3
|
||||
#endif
|
||||
|
||||
#ifndef debug_level_info
|
||||
# define debug_level_info 2
|
||||
#endif
|
||||
|
||||
#ifndef debug_level_error
|
||||
# define debug_level_error 1
|
||||
#endif
|
||||
|
||||
#define FUNC_NAME DEBUG_MSG_PREFIX __FUNCTION__ ": "
|
||||
|
||||
#define SHOW_FLOW(seriousness, format, param...) \
|
||||
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||
}} while( 0 )
|
||||
|
||||
#define SHOW_FLOW0(seriousness, format) \
|
||||
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||
}} while( 0 )
|
||||
|
||||
#define SHOW_INFO(seriousness, format, param...) \
|
||||
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||
}} while( 0 )
|
||||
|
||||
#define SHOW_INFO0(seriousness, format) \
|
||||
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||
}} while( 0 )
|
||||
|
||||
#define SHOW_ERROR(seriousness, format, param...) \
|
||||
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT_ERROR \
|
||||
}} while( 0 )
|
||||
|
||||
#define SHOW_ERROR0(seriousness, format) \
|
||||
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT_ERROR \
|
||||
}} while( 0 )
|
||||
|
||||
#endif /* _BENAPHORE_H */
|
Loading…
Reference in New Issue
Block a user