* Work in progress commit of a reworked ATA bus_manager. It's now object based,

but doesn't really do anything more than before.
* It also replaces everything IDE with ATA counterparts and cleans up a lot
  of the definitions.
* Cleaning up the old ATA bus_manager as well as some license headers missing.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30049 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-04-08 23:54:52 +00:00
parent b2a271d29f
commit bf9a383524
12 changed files with 2938 additions and 0 deletions

View File

@ -0,0 +1,70 @@
/*
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef __ATA_H__
#define __ATA_H__
#include <device_manager.h>
#include <KernelExport.h>
// Controller Driver Node
// attributes:
// node type
#define ATA_BUS_TYPE_NAME "bus/ide/v1"
// maximum number of devices connected to controller (uint8, optional, default:2)
#define ATA_CONTROLLER_MAX_DEVICES_ITEM "ide/max_devices"
// set to not-0 if DMA is supported (uint8, optional, default:0)
#define ATA_CONTROLLER_CAN_DMA_ITEM "ide/can_dma"
// name of controller (string, required)
#define ATA_CONTROLLER_CONTROLLER_NAME_ITEM "ide/controller_name"
union ata_task_file;
typedef unsigned int ata_reg_mask;
// channel cookie, issued by ata bus manager
typedef void *ata_channel;
// interface of controller driver
typedef struct {
driver_module_info info;
void (*set_channel)(void *cookie, ata_channel channel);
status_t (*write_command_block_regs)(void *channelCookie,
union ata_task_file *file, ata_reg_mask mask);
status_t (*read_command_block_regs)(void *channelCookie,
union ata_task_file *file, ata_reg_mask mask);
uint8 (*get_altstatus)(void *channelCookie);
status_t (*write_device_control)(void *channelCookie, uint8 val);
status_t (*write_pio)(void *channelCookie, uint16 *data, int count,
bool force16Bit);
status_t (*read_pio)(void *channelCookie, uint16 *data, int count,
bool force16Bit);
status_t (*prepare_dma)(void *channelCookie, const physical_entry *sg_list,
size_t sg_list_count, bool write);
status_t (*start_dma)(void *channelCookie);
status_t (*finish_dma)(void *channelCookie);
} ata_controller_interface;
// Interface for Controller Driver
// interface of bus manager as seen from controller driver
// use this interface as the fixed consumer of your controller driver
typedef struct {
driver_module_info info;
// status - status read from controller (_not_ alt_status, as reading
// normal status acknowledges IRQ request of device)
status_t (*interrupt_handler)(ata_channel channel, uint8 status);
} ata_for_controller_interface;
#define ATA_FOR_CONTROLLER_MODULE_NAME "bus_managers/ide/controller/driver_v1"
#endif /* __ATA_H__ */

View File

@ -0,0 +1,284 @@
/*
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef __ATA_TYPES_H__
#define __ATA_TYPES_H__
#include <iovec.h>
#include <lendian_bitfield.h>
// ATA task file.
// contains the command block interpreted under different conditions with
// first byte being first command register, second byte second command register
// etc.; for lba48, registers must be written twice, therefore there
// are twice as many bytes as registers - the first eight bytes are those
// that must be written first, the second eight bytes are those that
// must be written second.
union ata_task_file {
struct {
uint8 features;
uint8 sector_count;
uint8 sector_number;
uint8 cylinder_0_7;
uint8 cylinder_8_15;
LBITFIELD8_3(
head : 4,
device : 1,
mode : 3
);
uint8 command;
} chs;
struct {
uint8 features;
uint8 sector_count;
uint8 lba_0_7;
uint8 lba_8_15;
uint8 lba_16_23;
LBITFIELD8_3(
lba_24_27 : 4,
device : 1,
mode : 3
);
uint8 command;
} lba;
struct {
LBITFIELD8_3(
dma : 1,
ovl : 1,
_0_res2 : 6
);
LBITFIELD8_2(
_1_res0 : 3,
tag : 5
);
uint8 _2_res;
uint8 byte_count_0_7;
uint8 byte_count_8_15;
LBITFIELD8_6(
lun : 3,
_5_res3 : 1,
device : 1,
_5_one5 : 1,
_5_res6 : 1,
_5_one7 : 1
);
uint8 command;
} packet;
struct {
LBITFIELD8_5(
ili : 1,
eom : 1,
abrt : 1,
_0_res3 : 1,
sense_key : 4
);
LBITFIELD8_4(
cmd_or_data : 1, // 1 - cmd, 0 - data
input_or_output : 1, // 0 - input (to device), 1 - output
release : 1,
tag : 5
);
uint8 _2_res;
uint8 byte_count_0_7;
uint8 byte_count_8_15;
LBITFIELD8_5(
_4_res0 : 4,
device : 1,
_4_obs5 : 1,
_4_res6 : 1,
_4_obs7 : 1
);
LBITFIELD8_7(
chk : 1,
_7_res1 : 2,
drq : 1,
serv : 1,
dmrd : 1,
drdy : 1,
bsy : 1
);
} packet_res;
struct {
uint8 sector_count;
LBITFIELD8_4( // only <tag> is defined for write
cmd_or_data : 1, // 1 - cmd, 0 - data
input_or_output : 1, // 0 - input (to device), 1 - output
release : 1,
tag : 5
);
uint8 lba_0_7;
uint8 lba_8_15;
uint8 lba_16_23;
LBITFIELD8_3(
lba_24_27 : 4,
device : 1,
mode : 3
);
uint8 command;
} queued;
struct {
// low order bytes
uint8 features;
uint8 sector_count_0_7;
uint8 lba_0_7;
uint8 lba_8_15;
uint8 lba_16_23;
LBITFIELD8_3(
_5low_res0 : 4,
device : 1,
mode : 3
);
uint8 command;
// high order bytes
uint8 _0high_res;
uint8 sector_count_8_15;
uint8 lba_24_31;
uint8 lba_32_39;
uint8 lba_40_47;
} lba48;
struct {
// low order bytes
uint8 sector_count_0_7;
LBITFIELD8_4(
cmd_or_data : 1, // 1 - cmd, 0 - data
input_or_output : 1, // 0 - input (to device), 1 - output
release : 1,
tag : 5
);
uint8 lba_0_7;
uint8 lba_8_15;
uint8 lba_16_23;
LBITFIELD8_3(
_5low_res0 : 4,
device : 1,
mode : 3
);
uint8 command;
// high order bytes
uint8 sector_count_8_15;
uint8 _1high_res;
uint8 lba_24_31;
uint8 lba_32_39;
uint8 lba_40_47;
} queued48;
struct {
uint8 r[7+5];
} raw;
struct {
uint8 features;
uint8 sector_count;
uint8 sector_number;
uint8 cylinder_low;
uint8 cylinder_high;
uint8 device_head;
uint8 command;
} write;
struct {
uint8 error;
uint8 sector_count;
uint8 sector_number;
uint8 cylinder_low;
uint8 cylinder_high;
uint8 device_head;
uint8 status;
} read;
};
typedef union ata_task_file ata_task_file;
// content of "mode" field
enum {
ATA_MODE_CHS = 5,
ATA_MODE_LBA = 7
};
// mask for ata_task_file fields to be written
enum {
ATA_MASK_FEATURES = 0x01,
ATA_MASK_SECTOR_COUNT = 0x02,
// CHS
ATA_MASK_SECTOR_NUMBER = 0x04,
ATA_MASK_CYLINDER_LOW = 0x08,
ATA_MASK_CYLINDER_HIGH = 0x10,
// LBA
ATA_MASK_LBA_LOW = 0x04,
ATA_MASK_LBA_MID = 0x08,
ATA_MASK_LBA_HIGH = 0x10,
// packet
ATA_MASK_BYTE_COUNT = 0x18,
// packet and dma queued result
ATA_MASK_ERROR = 0x01,
ATA_MASK_IREASON = 0x02,
ATA_MASK_DEVICE_HEAD = 0x20,
ATA_MASK_COMMAND = 0x40,
ATA_MASK_STATUS = 0x40,
// for 48 bits, the following flags tell which registers to load twice
ATA_MASK_FEATURES_48 = 0x80 | ATA_MASK_FEATURES,
ATA_MASK_SECTOR_COUNT_48 = 0x100 | ATA_MASK_SECTOR_COUNT,
ATA_MASK_LBA_LOW_48 = 0x200 | ATA_MASK_LBA_LOW,
ATA_MASK_LBA_MID_48 = 0x400 | ATA_MASK_LBA_MID,
ATA_MASK_LBA_HIGH_48 = 0x800 | ATA_MASK_LBA_HIGH,
ATA_MASK_HOB = 0xf80
};
// status register
enum {
ATA_STATUS_ERROR = 0x01, // error
ATA_STATUS_INDEX = 0x02, // obsolete
ATA_STATUS_CORR = 0x04, // obsolete
ATA_STATUS_DATA_REQUEST = 0x08, // data request
ATA_STATUS_DSC = 0x10, // reserved
ATA_STATUS_SERVICE = 0x10, // ready to service device
ATA_STATUS_DWF = 0x20, // reserved
ATA_STATUS_DMA = 0x20, // reserved
ATA_STATUS_DMA_READY = 0x20, // packet: DMA ready
ATA_STATUS_DISK_FAILURE = 0x20, // packet: disk failure
ATA_STATUS_DEVICE_READY = 0x40, // device ready
ATA_STATUS_BUSY = 0x80 // busy
};
// device control register
enum {
// bit 0 must be zero
ATA_DEVICE_CONTROL_DISABLE_INTS = 0x02, // disable INTRQ
ATA_DEVICE_CONTROL_SOFT_RESET = 0x04, // software device reset
ATA_DEVICE_CONTROL_BIT3 = 0x08, // don't know, but must be set
// bits inbetween are reserved
ATA_DEVICE_CONTROL_HIGH_ORDER_BYTE = 0x80 // read high order byte
// (for 48-bit lba)
};
// error register - most bits are command specific
enum {
// always used
ATA_ERROR_ABORTED = 0x04, // command aborted
// used for Ultra DMA modes
ATA_ERROR_INTERFACE_CRC = 0x80, // interface CRC error
// used by reading data transfers
ATA_ERROR_UNCORRECTABLE = 0x40, // uncorrectable data error
// used by writing data transfers
ATA_ERROR_WRITE_PROTECTED = 0x40, // media write protect
// used by all data transfer commands
ATA_ERROR_MEDIUM_CHANGED = 0x20, // medium changed
ATA_ERROR_INVALID_ADDRESS = 0x10, // invalid CHS address
ATA_ERROR_MEDIA_CHANGE_REQUESTED = 0x08, // media change requested
ATA_ERROR_NO_MEDIA = 0x02, // no media
};
typedef struct ata_channel_info *ata_channel_cookie;
#endif /* __ATA_TYPES_H__ */

View File

@ -0,0 +1,888 @@
#include "ATAPrivate.h"
ATAChannel::ATAChannel(device_node *node)
: fNode(node),
fChannelID(0),
fController(NULL),
fCookie(NULL),
fStatus(B_NO_INIT),
fSCSIBus(NULL),
fDeviceCount(0),
fDevices(NULL),
fUseDMA(false),
fRequest(NULL)
{
mutex_init(&fLock, "ata channel");
if (fUseDMA) {
void *settings = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
if (settings != NULL) {
if (get_driver_boolean_parameter(settings,
B_SAFEMODE_DISABLE_IDE_DMA, false, false)) {
TRACE("disabling dma because of safemode setting\n");
fUseDMA = false;
}
unload_driver_settings(settings);
}
}
if (fUseDMA) {
uint8 canDMA;
if (gDeviceManager->get_attr_uint8(node, ATA_CONTROLLER_CAN_DMA_ITEM,
&canDMA, true) != B_OK || canDMA == 0) {
fUseDMA = false;
}
}
fRequest = new(std::nothrow) ATARequest();
if (fRequest == NULL) {
fStatus = B_NO_MEMORY;
return;
}
uint8 maxDevices = 2;
if (gDeviceManager->get_attr_uint8(node, ATA_CONTROLLER_MAX_DEVICES_ITEM,
&maxDevices, true) != B_OK) {
maxDevices = 2;
}
fDeviceCount = MIN(maxDevices, 2);
fDevices = new(std::nothrow) ATADevice *[fDeviceCount];
if (fDevices == NULL) {
fStatus = B_NO_MEMORY;
return;
}
for (uint8 i = 0; i < fDeviceCount; i++)
fDevices[i] = NULL;
gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID,
true);
snprintf(fDebugContext, sizeof(fDebugContext), " %lu", fChannelID);
device_node *parent = gDeviceManager->get_parent_node(node);
fStatus = gDeviceManager->get_driver(parent,
(driver_module_info **)&fController, &fCookie);
gDeviceManager->put_node(parent);
}
ATAChannel::~ATAChannel()
{
mutex_lock(&fLock);
if (fDevices) {
for (uint8 i = 0; i < fDeviceCount; i++)
delete fDevices[i];
delete [] fDevices;
}
delete fRequest;
mutex_destroy(&fLock);
}
status_t
ATAChannel::InitCheck()
{
return fStatus;
}
void
ATAChannel::SetBus(scsi_bus bus)
{
fSCSIBus = bus;
}
status_t
ATAChannel::ScanBus()
{
bool devicePresent[fDeviceCount];
uint32 deviceSignature[fDeviceCount];
status_t result = Reset(devicePresent, deviceSignature);
if (result != B_OK) {
TRACE_ERROR("resetting the channel failed\n");
return result;
}
for (uint8 i = 0; i < fDeviceCount; i++) {
if (!devicePresent[i])
continue;
ATADevice *device = NULL;
if (deviceSignature[i] == ATA_SIGNATURE_ATAPI)
device = new(std::nothrow) ATAPIDevice(this, i);
else
device = new(std::nothrow) ATADevice(this, i);
if (device == NULL)
return B_NO_MEMORY;
TRACE("trying ATA%s device %u\n", device->IsATAPI() ? "PI" : "", i);
bool identified = device->Identify() == B_OK;
if (!identified && !device->IsATAPI()) {
// retry as atapi
delete device;
device = new(std::nothrow) ATAPIDevice(this, i);
if (device == NULL)
return B_NO_MEMORY;
identified = device->Identify() == B_OK;
}
if (!identified) {
delete device;
continue;
}
TRACE_ALWAYS("identified ATA%s device %u\n", device->IsATAPI()
? "PI" : "", i);
if (device->Configure() != B_OK) {
TRACE_ERROR("failed to configure device\n");
delete device;
continue;
}
fDevices[i] = device;
}
return B_OK;
}
void
ATAChannel::PathInquiry(scsi_path_inquiry *info)
{
info->hba_inquiry = SCSI_PI_TAG_ABLE | SCSI_PI_WIDE_16;
info->hba_misc = 0;
info->sim_priv = 0;
info->initiator_id = 2;
info->hba_queue_size = 1;
memset(info->vuhba_flags, 0, sizeof(info->vuhba_flags));
strlcpy(info->sim_vid, "Haiku", SCSI_SIM_ID);
const char *controllerName = NULL;
if (gDeviceManager->get_attr_string(fNode,
SCSI_DESCRIPTION_CONTROLLER_NAME, &controllerName, true) == B_OK)
strlcpy(info->hba_vid, controllerName, SCSI_HBA_ID);
else
strlcpy(info->hba_vid, "unknown", SCSI_HBA_ID);
strlcpy(info->sim_version, "1.0", SCSI_VERS);
strlcpy(info->hba_version, "1.0", SCSI_VERS);
strlcpy(info->controller_family, "ATA", SCSI_FAM_ID);
strlcpy(info->controller_type, "ATA", SCSI_TYPE_ID);
}
void
ATAChannel::GetRestrictions(uint8 targetID, bool *isATAPI, bool *noAutoSense,
uint32 *maxBlocks)
{
// we always indicate ATAPI so we have to emulate fewer commands
*isATAPI = true;
*noAutoSense = false;
if (targetID < fDeviceCount && fDevices[targetID] != NULL
&& fDevices[targetID]->IsATAPI()) {
*noAutoSense = true;
}
*maxBlocks = 255;
}
status_t
ATAChannel::ExecuteIO(scsi_ccb *ccb)
{
TRACE_FUNCTION("%p\n", ccb);
if (mutex_trylock(&fLock) != B_OK)
return B_BUSY;
MutexLocker _(&fLock, true);
fRequest->SetCCB(ccb);
if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE) {
fRequest->RequestSense();
fRequest->Finish(false);
return B_OK;
}
if (ccb->target_id >= fDeviceCount) {
TRACE_ERROR("invalid target device\n");
fRequest->SetStatus(SCSI_SEL_TIMEOUT);
fRequest->Finish(false);
return B_BAD_INDEX;
}
ATADevice *device = fDevices[ccb->target_id];
if (device == NULL) {
TRACE_ERROR("target device not present\n");
fRequest->SetStatus(SCSI_SEL_TIMEOUT);
fRequest->Finish(false);
return B_BAD_INDEX;
}
fRequest->SetTimeout(ccb->timeout > 0 ? ccb->timeout * 1000 * 1000
: ATA_STANDARD_TIMEOUT);
status_t result = device->ExecuteIO(fRequest);
fRequest->Finish(false);
return result;
}
status_t
ATAChannel::SelectDevice(uint8 device)
{
TRACE_FUNCTION("device: %u\n", device);
if (device > 1)
return B_BAD_INDEX;
ata_task_file taskFile;
taskFile.chs.head = 0;
taskFile.chs.mode = ATA_MODE_LBA;
taskFile.chs.device = device;
_WriteRegs(&taskFile, ATA_MASK_DEVICE_HEAD);
_FlushAndWait(1);
#if KDEBUG > 0
// for debugging only
_ReadRegs(&taskFile, ATA_MASK_DEVICE_HEAD);
if (taskFile.chs.device != device) {
TRACE_ERROR("device %d not selected! head 0x%x, mode 0x%x, device %d\n",
device, taskFile.chs.head, taskFile.chs.mode, taskFile.chs.device);
return B_ERROR;
}
#endif
return B_OK;
}
bool
ATAChannel::IsDevicePresent(uint8 device)
{
if (SelectDevice(device) != B_OK)
return false;
ata_task_file taskFile;
taskFile.chs.device = device;
taskFile.chs.mode = ATA_MODE_LBA;
taskFile.chs.command = ATA_COMMAND_NOP;
_WriteRegs(&taskFile, ATA_MASK_DEVICE_HEAD);
_FlushAndWait(10);
_ReadRegs(&taskFile, ATA_MASK_STATUS | ATA_MASK_ERROR);
TRACE("status: 0x%02x; error: 0x%02x\n", taskFile.read.status,
taskFile.read.error);
return (taskFile.read.status & 0xf8) != 0xf8
&& taskFile.read.status != 0xa5;
}
status_t
ATAChannel::Reset(bool *presence, uint32 *signatures)
{
TRACE_FUNCTION("%p, %p\n", presence, signatures);
bool devicePresent[fDeviceCount];
for (uint8 i = 0; i < fDeviceCount; i++) {
devicePresent[i] = IsDevicePresent(i);
TRACE("device %d: %s present\n", i, devicePresent[i] ? "might be" : "is not");
}
SelectDevice(0);
// disable interrupts and assert SRST for at least 5 usec
if (_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS
| ATA_DEVICE_CONTROL_SOFT_RESET) != B_OK) {
TRACE_ERROR("failed to set reset signaling\n");
return B_ERROR;
}
_FlushAndWait(20);
// clear reset and wait for at least 2 ms (wait 150ms like everyone else)
if (_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS) != B_OK) {
TRACE_ERROR("failed to clear reset signaling\n");
return B_ERROR;
}
_FlushAndWait(150 * 1000);
for (uint8 i = 0; i < fDeviceCount; i++) {
if (presence != NULL)
presence[i] = devicePresent[i];
if (!devicePresent[i])
continue;
SelectDevice(i);
// wait up to 31 seconds for busy to clear
if (Wait(0, ATA_STATUS_BUSY, 0, 31 * 1000 * 1000) != B_OK) {
TRACE_ERROR("reset timeout\n");
return B_TIMED_OUT;
}
ata_task_file taskFile;
if (_ReadRegs(&taskFile, ATA_MASK_SECTOR_COUNT | ATA_MASK_LBA_LOW
| ATA_MASK_LBA_MID | ATA_MASK_LBA_HIGH | ATA_MASK_ERROR) != B_OK) {
TRACE_ERROR("reading status failed\n");
return B_ERROR;
}
if (taskFile.read.error != 0x01
&& (i > 0 || taskFile.read.error != 0x81)) {
TRACE_ERROR("device %d failed, error code is 0x%02x\n", i,
taskFile.read.error);
}
if (i == 0 && taskFile.read.error >= 0x80) {
TRACE_ERROR("device %d indicates that other device failed"
" with code 0x%02x\n", i, taskFile.read.error);
}
if (signatures != NULL) {
signatures[i] = taskFile.lba.sector_count
| (((uint32)taskFile.lba.lba_0_7) << 8)
| (((uint32)taskFile.lba.lba_8_15) << 16)
| (((uint32)taskFile.lba.lba_16_23) << 24);
}
}
return B_OK;
}
status_t
ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags,
bigtime_t timeout)
{
bigtime_t startTime = system_time();
_FlushAndWait(1);
while (true) {
uint8 status = _AltStatus();
if ((flags & ATA_CHECK_ERROR_BIT) != 0
&& (status & ATA_STATUS_ERROR) != 0)
return B_ERROR;
if ((status & setBits) == setBits && (status & clearedBits) == 0)
return B_OK;
bigtime_t elapsedTime = system_time() - startTime;
//TRACE("wait status after %lld: %u\n", elapsedTime, status);
if (elapsedTime > timeout)
return B_TIMED_OUT;
// The device may be ready almost immediatelly. If it isn't,
// poll often during the first 20ms, otherwise poll lazyly.
if (elapsedTime < 500)
spin(1);
else if (elapsedTime < 20000)
snooze(1000);
else
snooze(50000);
}
return B_ERROR;
}
status_t
ATAChannel::WaitDataRequest(bool high)
{
return Wait(high ? ATA_STATUS_DATA_REQUEST : 0,
high ? 0 : ATA_STATUS_DATA_REQUEST,
ATA_CHECK_ERROR_BIT, (high ? 10 : 1) * 1000 * 1000);
}
status_t
ATAChannel::WaitDeviceReady()
{
return Wait(ATA_STATUS_DEVICE_READY, 0, 0, 5 * 1000 * 1000);
}
status_t
ATAChannel::WaitForIdle()
{
return Wait(0, ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST, 0, 20 * 1000);
}
status_t
ATAChannel::SendRequest(ATARequest *request, uint32 flags)
{
ATADevice *device = request->Device();
if (device->Select() != B_OK || WaitForIdle() != B_OK) {
// resetting the device here will discard current configuration,
// it's better when the SCSI bus manager requests an external reset.
TRACE_ERROR("device selection timeout\n");
request->SetStatus(SCSI_SEL_TIMEOUT);
return B_TIMED_OUT;
}
if ((flags & ATA_DEVICE_READY_REQUIRED) != 0
&& (_AltStatus() & ATA_STATUS_DEVICE_READY) == 0) {
TRACE_ERROR("device ready not set\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
if (_WriteRegs(device->TaskFile(), device->RegisterMask()
| ATA_MASK_COMMAND) != B_OK) {
TRACE_ERROR("can't write command\n");
request->SetStatus(SCSI_HBA_ERR);
return B_ERROR;
}
return B_OK;
}
status_t
ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
{
if (flags & ATA_WAIT_FINISH) {
// wait for the device to finish current command (device no longer busy)
status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
if (result != B_OK) {
TRACE_ERROR("timeout\n");
request->SetStatus(SCSI_CMD_TIMEOUT);
return result;
}
}
ata_task_file *taskFile = request->Device()->TaskFile();
// read status, this also acknowledges pending interrupts
status_t result = _ReadRegs(taskFile, ATA_MASK_STATUS | ATA_MASK_ERROR);
if (result != B_OK) {
TRACE("reading status failed\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return result;
}
if (taskFile->read.status & ATA_STATUS_BUSY) {
TRACE("command failed, device still busy\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
if ((flags & ATA_DEVICE_READY_REQUIRED)
&& (taskFile->read.status & ATA_STATUS_DEVICE_READY) == 0) {
TRACE("command failed, device ready required but not set\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
if ((taskFile->read.status & ATA_STATUS_ERROR) == 0)
return B_OK;
request->SetStatus(SCSI_SEQUENCE_FAIL);
TRACE_ERROR("command failed, error bit is set\n");
uint8 error = taskFile->read.error & errorMask;
if (error & ATA_ERROR_INTERFACE_CRC) {
TRACE_ERROR("interface crc error\n");
request->SetSense(SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_COM_CRC);
return B_ERROR;
}
if (flags & ATA_IS_WRITE) {
if (error & ATA_ERROR_WRITE_PROTECTED) {
request->SetSense(SCSIS_KEY_DATA_PROTECT, SCSIS_ASC_WRITE_PROTECTED);
return B_ERROR;
}
} else {
if (error & ATA_ERROR_UNCORRECTABLE) {
request->SetSense(SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_UNREC_READ_ERR);
return B_ERROR;
}
}
if (error & ATA_ERROR_MEDIUM_CHANGED) {
request->SetSense(SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_MEDIUM_CHANGED);
return B_ERROR;
}
if (error & ATA_ERROR_INVALID_ADDRESS) {
// XXX strange error code, don't really know what it means
request->SetSense(SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_RANDOM_POS_ERROR);
return B_ERROR;
}
if (error & ATA_ERROR_MEDIA_CHANGE_REQUESTED) {
request->SetSense(SCSIS_KEY_UNIT_ATTENTION, SCSIS_ASC_REMOVAL_REQUESTED);
return B_ERROR;
}
if (error & ATA_ERROR_NO_MEDIA) {
request->SetSense(SCSIS_KEY_MEDIUM_ERROR, SCSIS_ASC_NO_MEDIUM);
return B_ERROR;
}
if (error & ATA_ERROR_ABORTED) {
request->SetSense(SCSIS_KEY_ABORTED_COMMAND, SCSIS_ASC_NO_SENSE);
return B_ERROR;
}
// either there was no error bit set or it was masked out
request->SetSense(SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE);
return B_ERROR;
}
status_t
ATAChannel::ExecuteDMATransfer(ATARequest *request)
{
TRACE_ERROR("dma transfers unimplemented\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
status_t
ATAChannel::ExecutePIOTransfer(ATARequest *request)
{
bigtime_t timeout = request->Timeout();
status_t result = B_OK;
uint32 *blocksLeft = request->BlocksLeft();
while (*blocksLeft > 0) {
if (Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 0, timeout) != B_OK) {
TRACE_ERROR("timeout waiting for device to request data\n");
result = B_TIMED_OUT;
break;
}
if (request->IsWrite()) {
result = _WritePIOBlock(request, 512);
if (result != B_OK) {
TRACE_ERROR("failed to write pio block\n");
break;
}
} else {
result = _ReadPIOBlock(request, 512);
if (result != B_OK) {
TRACE_ERROR("failed to read pio block\n");
break;
}
}
(*blocksLeft)--;
// wait 1 pio cycle
if (*blocksLeft > 0)
_AltStatus();
}
if (result == B_OK && WaitDataRequest(false) != B_OK) {
TRACE_ERROR("device still expects data transfer\n");
result = B_ERROR;
}
if (result == B_OK) {
result = FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}
if (result != B_OK)
request->SetStatus(SCSI_SEQUENCE_FAIL);
return result;
}
status_t
ATAChannel::ReadPIO(uint8 *buffer, size_t length)
{
return fController->read_pio(fCookie, (uint16 *)buffer,
length / sizeof(uint16), false);
}
status_t
ATAChannel::_ReadRegs(ata_task_file *taskFile, ata_reg_mask mask)
{
return fController->read_command_block_regs(fCookie, taskFile, mask);
}
status_t
ATAChannel::_WriteRegs(ata_task_file *taskFile, ata_reg_mask mask)
{
return fController->write_command_block_regs(fCookie, taskFile, mask);
}
status_t
ATAChannel::_WriteControl(uint8 value)
{
return fController->write_device_control(fCookie, ATA_DEVICE_CONTROL_BIT3
| value);
}
uint8
ATAChannel::_AltStatus()
{
return fController->get_altstatus(fCookie);
}
void
ATAChannel::_FlushAndWait(bigtime_t waitTime)
{
_AltStatus();
if (waitTime > 100)
snooze(waitTime);
else
spin(waitTime);
}
status_t
ATAChannel::_ReadPIOBlock(ATARequest *request, size_t length)
{
uint32 transferred = 0;
status_t result = _TransferPIOBlock(request, length, &transferred);
request->CCB()->data_resid -= transferred;
// if length was odd, there's an extra byte waiting in request->OddByte()
if (request->GetOddByte(NULL)) {
// discard byte and adjust res_id as the extra byte didn't reach the
// buffer
request->CCB()->data_resid++;
}
if (result != B_BUFFER_OVERFLOW)
return result;
// 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 B_OK;
TRACE_ERROR("pio read: discarding after %lu bytes", transferred);
uint8 buffer[32];
length -= transferred;
// discard 32 bytes at once (see _WritePIOBlock())
while (length > 0) {
// read extra byte if length is odd (that's the "length + 1")
size_t currentLength = MIN(length + 1, (uint32)sizeof(buffer))
/ sizeof(uint16);
fController->read_pio(fCookie, (uint16 *)buffer, currentLength, false);
length -= currentLength * 2;
}
return B_OK;
}
status_t
ATAChannel::_WritePIOBlock(ATARequest *request, size_t length)
{
size_t transferred = 0;
status_t result = _TransferPIOBlock(request, length, &transferred);
request->CCB()->data_resid -= transferred;
if (result != B_BUFFER_OVERFLOW)
return result;
// there may be a pending odd byte - transmit that now
uint8 byte;
if (request->GetOddByte(&byte)) {
uint8 buffer[2];
buffer[0] = byte;
buffer[1] = 0;
fController->write_pio(fCookie, (uint16 *)buffer, 1, false);
request->CCB()->data_resid--;
transferred += 2;
}
// "transferred" may actually be larger then length because the last odd-byte
// is sent together with an extra zero-byte
if (transferred >= length)
return B_OK;
// 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
static const uint8 buffer[32] = {};
TRACE_ERROR("pio write: discarding after %lu bytes", transferred);
length -= transferred;
while (length > 0) {
// if device asks for odd number of bytes, append an extra byte to
// make length even (this is the "length + 1" term)
size_t currentLength = MIN(length + 1, (int)(sizeof(buffer)))
/ sizeof(uint16);
fController->write_pio(fCookie, (uint16 *)buffer, currentLength, false);
length -= currentLength * 2;
}
return B_BUFFER_OVERFLOW;
}
status_t
ATAChannel::_TransferPIOBlock(ATARequest *request, size_t length,
size_t *transferred)
{
// data is usually split up into multiple scatter/gather blocks
while (length > 0) {
if (request->SGElementsLeft() == 0) {
// ups - buffer too small (for ATAPI data, this is OK)
return B_BUFFER_OVERFLOW;
}
// we might have transmitted part of a scatter/entry already
const physical_entry *entry = request->CurrentSGElement();
uint32 offset = request->CurrentSGOffset();
uint32 currentLength = MIN(entry->size - offset, length);
status_t result = _TransferPIOPhysical(request,
(addr_t)entry->address + offset, currentLength, transferred);
if (result != B_OK) {
request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
SCSIS_ASC_INTERNAL_FAILURE);
return result;
}
request->AdvanceSG(currentLength);
length -= currentLength;
}
return B_OK;
}
// TODO: this should not be necessary, we could directly use virtual addresses
#include <vm.h>
#include <thread.h>
status_t
ATAChannel::_TransferPIOPhysical(ATARequest *request, addr_t physicalAddress,
size_t length, size_t *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) {
struct thread *thread = thread_get_current_thread();
thread_pin_to_current_cpu(thread);
void *handle;
addr_t virtualAddress;
if (vm_get_physical_page_current_cpu(physicalAddress, &virtualAddress,
&handle) != B_OK) {
thread_unpin_from_current_cpu(thread);
// ouch: this should never ever happen
return B_ERROR;
}
ASSERT(physicalAddress % B_PAGE_SIZE == virtualAddress % B_PAGE_SIZE);
// if chunk starts in the middle of a page, we have even less then
// a page left
size_t pageLeft = B_PAGE_SIZE - physicalAddress % B_PAGE_SIZE;
size_t currentLength = MIN(pageLeft, length);
status_t result = _TransferPIOVirtual(request, (uint8 *)virtualAddress,
currentLength, transferred);
vm_put_physical_page_current_cpu(virtualAddress, handle);
thread_unpin_from_current_cpu(thread);
if (result != B_OK)
return result;
length -= currentLength;
physicalAddress += currentLength;
}
return B_OK;
}
status_t
ATAChannel::_TransferPIOVirtual(ATARequest *request, uint8 *virtualAddress,
size_t length, size_t *transferred)
{
if (request->IsWrite()) {
// 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)
uint8 byte;
if (request->GetOddByte(&byte)) {
uint8 buffer[2];
buffer[0] = byte;
buffer[1] = *virtualAddress++;
fController->write_pio(fCookie, (uint16 *)buffer, 1, false);
length--;
*transferred += 2;
}
fController->write_pio(fCookie, (uint16 *)virtualAddress, length / 2,
false);
// take care if chunk size was odd, which means that 1 byte remains
virtualAddress += length & ~1;
*transferred += length & ~1;
if ((length & 1) != 0)
request->SetOddByte(*virtualAddress);
} else {
// if we read one byte too much last time, push it into current chunk
uint8 byte;
if (request->GetOddByte(&byte)) {
*virtualAddress++ = byte;
length--;
}
fController->read_pio(fCookie, (uint16 *)virtualAddress, length / 2,
false);
// take care of odd chunk size;
// in this case we read 1 byte to few!
virtualAddress += length & ~1;
*transferred += length & ~1;
if ((length & 1) != 0) {
uint8 buffer[2];
// now read the missing byte; as we have to read 2 bytes at once,
// we'll read one byte too much
fController->read_pio(fCookie, (uint16 *)buffer, 1, false);
*virtualAddress = buffer[0];
request->SetOddByte(buffer[1]);
*transferred += 2;
}
}
return B_OK;
}

View File

@ -0,0 +1,50 @@
#ifndef ATA_COMMANDS_H
#define ATA_COMMANDS_H
#define ATA_COMMAND_WRITE_DMA 0xca
#define ATA_COMMAND_WRITE_DMA_QUEUED 0xcc
#define ATA_COMMAND_WRITE_MULTIPLE 0xc5
#define ATA_COMMAND_WRITE_SECTORS 0x30
#define ATA_COMMAND_READ_DMA 0xc8
#define ATA_COMMAND_READ_DMA_QUEUED 0xc7
#define ATA_COMMAND_READ_MULTIPLE 0xc4
#define ATA_COMMAND_READ_SECTORS 0x20
#define ATA_COMMAND_WRITE_DMA_EXT 0x35
#define ATA_COMMAND_WRITE_DMA_QUEUED_EXT 0x36
#define ATA_COMMAND_WRITE_MULTIPLE_EXT 0x39
#define ATA_COMMAND_WRITE_SECTORS_EXT 0x34
#define ATA_COMMAND_READ_DMA_EXT 0x25
#define ATA_COMMAND_READ_DMA_QUEUED_EXT 0x26
#define ATA_COMMAND_READ_MULTIPLE_EXT 0x29
#define ATA_COMMAND_READ_SECTORS_EXT 0x24
#define ATA_COMMAND_PACKET 0xa0
#define ATA_COMMAND_DEVICE_RESET 0x08
#define ATA_COMMAND_SERVICE 0xa2
#define ATA_COMMAND_NOP 0
#define ATA_COMMAND_NOP_NOP 0
#define ATA_COMMAND_NOP_NOP_AUTOPOLL 1
#define ATA_COMMAND_GET_MEDIA_STATUS 0xda
#define ATA_COMMAND_FLUSH_CACHE 0xe7
#define ATA_COMMAND_FLUSH_CACHE_EXT 0xea
#define ATA_COMMAND_MEDIA_EJECT 0xed
#define ATA_COMMAND_IDENTIFY_PACKET_DEVICE 0xa1
#define ATA_COMMAND_IDENTIFY_DEVICE 0xec
#define ATA_COMMAND_SET_FEATURES 0xef
#define ATA_COMMAND_SET_FEATURES_ENABLE_RELELEASE_INT 0x5d
#define ATA_COMMAND_SET_FEATURES_ENABLE_SERVICE_INT 0x5e
#define ATA_COMMAND_SET_FEATURES_DISABLE_RELEASE_INT 0xdd
#define ATA_COMMAND_SET_FEATURES_DISABLE_SERVICE_INT 0xde
#endif // ATA_COMMANDS_H

View File

@ -0,0 +1,585 @@
#include "ATAPrivate.h"
ATADevice::ATADevice(ATAChannel *channel, uint8 index)
: fChannel(channel),
fIndex(index),
fUseLBA(false),
fUse48Bits(false),
fUseDMA(channel->UseDMA()),
fTotalSectors(0),
fRegisterMask(0)
{
memset(&fInfoBlock, 0, sizeof(fInfoBlock));
memset(&fTaskFile, 0, sizeof(fTaskFile));
snprintf(fDebugContext, sizeof(fDebugContext), "%s %lu-%u",
IsATAPI() ? "pi " : "", channel->ChannelID(), index);
}
ATADevice::~ATADevice()
{
}
status_t
ATADevice::TestUnitReady(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
fRegisterMask = 0;
fTaskFile.write.command = ATA_COMMAND_GET_MEDIA_STATUS;
request->SetTimeout(15 * 1000 * 1000);
status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
if (result != B_OK) {
TRACE_ERROR("failed to send test unit ready request\n");
return result;
}
return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_NO_MEDIA | ATA_ERROR_ABORTED
| ATA_ERROR_MEDIA_CHANGE_REQUESTED | ATA_ERROR_MEDIUM_CHANGED);
}
status_t
ATADevice::SynchronizeCache(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
// 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 (!fInfoBlock.write_cache_supported)
return B_OK;
fRegisterMask = 0;
fTaskFile.lba.command = fUse48Bits ? ATA_COMMAND_FLUSH_CACHE_EXT
: ATA_COMMAND_FLUSH_CACHE;
request->SetTimeout(60 * 1000 * 1000);
status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
if (result != B_OK) {
TRACE_ERROR("failed to send synchronize cache request\n");
return result;
}
return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}
status_t
ATADevice::Eject(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
fRegisterMask = 0;
fTaskFile.lba.command = ATA_COMMAND_MEDIA_EJECT;
request->SetTimeout(15 * 1000 * 1000);
status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
if (result != B_OK) {
TRACE_ERROR("failed to send eject request\n");
return result;
}
return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED | ATA_ERROR_NO_MEDIA);
}
status_t
ATADevice::Inquiry(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
scsi_ccb *ccb = request->CCB();
scsi_cmd_inquiry *command = (scsi_cmd_inquiry *)ccb->cdb;
if (command->evpd || command->page_code) {
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}
scsi_res_inquiry data;
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.removable_medium = false;
data.ansi_version = 2;
data.ecma_version = 0;
data.iso_version = 0;
data.response_data_format = 2;
data.term_iop = false;
// to be changed if we support TERM I/O
data.additional_length = sizeof(scsi_res_inquiry) - 4;
data.soft_reset = false;
data.cmd_queue = 0;
data.linked = false;
// these values are free-style
data.sync = false;
data.write_bus16 = true;
data.write_bus32 = false;
data.relative_address = false;
// the following fields are *much* to small, sigh...
memcpy(data.vendor_ident, fInfoBlock.model_number,
sizeof(data.vendor_ident));
memcpy(data.product_ident, fInfoBlock.model_number + 8,
sizeof(data.product_ident));
memcpy(data.product_rev, " ", sizeof(data.product_rev));
uint32 allocationLength = command->allocation_length;
copy_sg_data(ccb, 0, allocationLength, &data, sizeof(data), false);
ccb->data_resid = ccb->data_length - MIN(MIN(sizeof(data),
allocationLength), ccb->data_length);
return B_OK;
}
status_t
ATADevice::ReadCapacity(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
scsi_ccb *ccb = request->CCB();
scsi_cmd_read_capacity *command = (scsi_cmd_read_capacity *)ccb->cdb;
if (command->pmi || command->lba) {
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}
scsi_res_read_capacity data;
data.block_size = B_HOST_TO_BENDIAN_INT32(512);
// TODO: 512 bytes fixed block size?
uint32 lastBlock = fTotalSectors - 1;
data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock);
TRACE("returning last block: %lu\n", B_BENDIAN_TO_HOST_INT32(data.lba));
copy_sg_data(ccb, 0, ccb->data_length, &data, sizeof(data), false);
ccb->data_resid = MAX(ccb->data_length - sizeof(data), 0);
return B_OK;
}
status_t
ATADevice::ExecuteIO(ATARequest *request)
{
TRACE_FUNCTION("%p\n", request);
scsi_ccb *ccb = request->CCB();
request->SetDevice(this);
// ATA devices have one LUN only
if (ccb->target_lun != 0) {
TRACE_ERROR("invalid target lun %d for ATA device\n", ccb->target_lun);
request->SetStatus(SCSI_SEL_TIMEOUT);
return B_BAD_INDEX;
}
TRACE("request: 0x%02x\n", ccb->cdb[0]);
request->ClearSense();
switch (ccb->cdb[0]) {
case SCSI_OP_TEST_UNIT_READY:
return TestUnitReady(request);
case SCSI_OP_FORMAT: /* FORMAT UNIT */
// we could forward ccb to disk, but modern disks cannot
// be formatted anyway, so we just refuse ccb
// (exceptions are removable media devices, but to my knowledge
// they don't have to be formatted as well)
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
return B_ERROR;
case SCSI_OP_INQUIRY:
return Inquiry(request);
case SCSI_OP_START_STOP:
{
scsi_cmd_ssu *command = (scsi_cmd_ssu *)ccb->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 (!command->start) {
// we must always flush cache if start = 0
SynchronizeCache(request);
}
if (command->load_eject) {
if (!command->start)
return Eject(request);
else {
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
SCSIS_ASC_PARAM_NOT_SUPPORTED);
return B_ERROR;
}
}
return B_OK;
}
case SCSI_OP_READ_CAPACITY:
return ReadCapacity(request);
case SCSI_OP_SYNCHRONIZE_CACHE:
// we ignore range and immediate bit, we always immediately
// flush everything
return SynchronizeCache(request);
// 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 *command = (scsi_cmd_rw_6 *)ccb->cdb;
uint32 address = ((uint32)command->high_lba << 16)
| ((uint32)command->mid_lba << 8) | (uint32)command->low_lba;
request->SetIsWrite(command->opcode == SCSI_OP_WRITE_6);
return ExecuteReadWrite(request, address, command->length != 0
? command->length : 256);
}
case SCSI_OP_READ_10:
case SCSI_OP_WRITE_10:
{
scsi_cmd_rw_10 *command = (scsi_cmd_rw_10 *)ccb->cdb;
uint32 address = B_BENDIAN_TO_HOST_INT32(command->lba);
uint32 sectorCount = B_BENDIAN_TO_HOST_INT16(command->length);
request->SetIsWrite(command->opcode == SCSI_OP_WRITE_10);
if (sectorCount > 0)
return ExecuteReadWrite(request, address, sectorCount);
else {
// we cannot transfer zero blocks (apart from LBA48)
request->SetStatus(SCSI_REQ_CMP);
return B_OK;
}
}
}
TRACE("command not implemented\n");
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
return B_ERROR;
}
status_t
ATADevice::Select()
{
return fChannel->SelectDevice(fIndex);
}
status_t
ATADevice::SetFeature(int feature)
{
TRACE("device_set_feature: feature %d\n", feature);
ATARequest request;
request.SetDevice(this);
request.SetTimeout(1 * 1000 * 1000);
fTaskFile.write.features = feature;
fTaskFile.write.command = ATA_COMMAND_SET_FEATURES;
fRegisterMask = ATA_MASK_FEATURES;
status_t result = fChannel->SendRequest(&request,
ATA_DEVICE_READY_REQUIRED);
if (result != B_OK) {
TRACE_ERROR("sending set feature request failed\n");
return result;
}
result = fChannel->FinishRequest(&request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
if (result != B_OK) {
TRACE_ERROR("set feature request failed\n");
return result;
}
return B_OK;
}
status_t
ATADevice::DisableCommandQueueing()
{
if (!fInfoBlock.DMA_QUEUED_supported)
return B_OK;
if (fInfoBlock.RELEASE_irq_supported) {
status_t result = SetFeature(
ATA_COMMAND_SET_FEATURES_DISABLE_RELEASE_INT);
if (result != B_OK) {
TRACE_ERROR("failed to disable release interrupt\n");
return result;
}
}
if (fInfoBlock.SERVICE_irq_supported) {
status_t result = SetFeature(
ATA_COMMAND_SET_FEATURES_DISABLE_SERVICE_INT);
if (result != B_OK) {
TRACE_ERROR("failed to disable service interrupt\n");
return result;
}
}
return B_OK;
}
status_t
ATADevice::ConfigureDMA()
{
if (!fUseDMA)
return B_OK;
fUseDMA = false;
return B_OK;
}
status_t
ATADevice::Configure()
{
// warning: ata == 0 means "this is ata"...
if (fInfoBlock._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 *)&fInfoBlock == 0x848a)
fInfoBlock.CFA_supported = true;
else
return B_ERROR;
}
if (!fInfoBlock._54_58_valid) {
// normally, current_xxx contains active CHS mapping,
// but if BIOS didn't call INITIALIZE DEVICE PARAMETERS
// the default mapping is used
fInfoBlock.current_sectors = fInfoBlock.sectors;
fInfoBlock.current_cylinders = fInfoBlock.cylinders;
fInfoBlock.current_heads = fInfoBlock.heads;
}
// just in case capacity_xxx isn't initialized - calculate it manually
// (seems that this information is really redundant; hopefully)
uint32 chsCapacity = fInfoBlock.current_sectors
* fInfoBlock.current_cylinders * fInfoBlock.current_heads;
fInfoBlock.capacity_low = chsCapacity & 0xff;
fInfoBlock.capacity_high = chsCapacity >> 8;
// checking LBA_supported flag should be sufficient, but it seems
// that checking LBA_total_sectors is a good idea
fUseLBA = fInfoBlock.LBA_supported && fInfoBlock.LBA_total_sectors != 0;
if (fUseLBA) {
fTotalSectors = fInfoBlock.LBA_total_sectors;
fTaskFile.lba.mode = ATA_MODE_LBA;
} else {
fTotalSectors = chsCapacity;
fTaskFile.chs.mode = ATA_MODE_CHS;
}
fUse48Bits = fInfoBlock._48_bit_addresses_supported;
if (fUse48Bits)
fTotalSectors = fInfoBlock.LBA48_total_sectors;
status_t result = ConfigureDMA();
if (result != B_OK)
return result;
result = DisableCommandQueueing();
if (result != B_OK)
return result;
return B_OK;
}
status_t
ATADevice::Identify()
{
ATARequest request;
request.SetDevice(this);
request.SetTimeout(20 * 1000 * 1000);
fRegisterMask = 0;
fTaskFile.write.command = IsATAPI() ? ATA_COMMAND_IDENTIFY_PACKET_DEVICE
: ATA_COMMAND_IDENTIFY_DEVICE;
if (fChannel->SendRequest(&request, IsATAPI() ? 0
: ATA_DEVICE_READY_REQUIRED) != B_OK) {
TRACE_ERROR("sending identify request failed\n");
return B_ERROR;
}
if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 0,
IsATAPI() ? 20 * 1000 * 1000 : 500 * 1000) != B_OK) {
TRACE_ERROR("timeout waiting for identify request\n");
return B_TIMED_OUT;
}
// get the infoblock
fChannel->ReadPIO((uint8 *)&fInfoBlock, sizeof(fInfoBlock));
if (fChannel->WaitDataRequest(false) != B_OK) {
TRACE_ERROR("device disagrees on info block length\n");
return B_ERROR;
}
if (fChannel->FinishRequest(&request, ATA_WAIT_FINISH | (IsATAPI() ? 0
: ATA_DEVICE_READY_REQUIRED), ATA_ERROR_ABORTED) != B_OK) {
TRACE_ERROR("failed to finish identify request\n");
return B_ERROR;
}
return B_OK;
}
status_t
ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
uint32 sectorCount)
{
request->SetUseDMA(fUseDMA && _PrepareDMA(request) == B_OK);
if (!request->UseDMA()) {
status_t result = _PreparePIO(request);
if (result != B_OK) {
TRACE_ERROR("failed to prepare pio transfer\n");
return result;
}
}
request->SetBlocksLeft(sectorCount);
if (_FillTaskFile(request, address) != B_OK) {
TRACE_ERROR("failed to setup transfer request\n");
return B_ERROR;
}
uint32 flags = 0;
if (!IsATAPI())
flags |= ATA_DEVICE_READY_REQUIRED;
if (request->UseDMA())
flags |= ATA_DMA_TRANSFER;
status_t result = fChannel->SendRequest(request, flags);
if (result != B_OK) {
TRACE_ERROR("failed to send transfer request\n");
return result;
}
if (request->UseDMA())
return fChannel->ExecuteDMATransfer(request);
return fChannel->ExecutePIOTransfer(request);
}
status_t
ATADevice::_FillTaskFile(ATARequest *request, uint64 address)
{
// list of LBA48 opcodes
static const uint8 s48BitCommands[2][2] = {
{ ATA_COMMAND_READ_SECTORS_EXT, ATA_COMMAND_WRITE_SECTORS_EXT },
{ ATA_COMMAND_READ_DMA_EXT, ATA_COMMAND_WRITE_DMA_EXT }
};
// list of normal LBA opcodes
static const uint8 s28BitCommands[2][2] = {
{ ATA_COMMAND_READ_SECTORS, ATA_COMMAND_WRITE_SECTORS },
{ ATA_COMMAND_READ_DMA, ATA_COMMAND_WRITE_DMA }
};
uint32 sectorCount = *request->BlocksLeft();
if (fUseLBA) {
if (fUse48Bits
&& (address + sectorCount > 0xfffffff || sectorCount > 0x100)) {
// use LBA48 only if necessary
if (sectorCount > 0xffff) {
TRACE_ERROR("invalid sector count %lu\n", sectorCount);
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}
fRegisterMask = ATA_MASK_SECTOR_COUNT_48
| ATA_MASK_LBA_LOW_48
| ATA_MASK_LBA_MID_48
| ATA_MASK_LBA_HIGH_48;
fTaskFile.lba48.sector_count_0_7 = sectorCount & 0xff;
fTaskFile.lba48.sector_count_8_15 = (sectorCount >> 8) & 0xff;
fTaskFile.lba48.lba_0_7 = address & 0xff;
fTaskFile.lba48.lba_8_15 = (address >> 8) & 0xff;
fTaskFile.lba48.lba_16_23 = (address >> 16) & 0xff;
fTaskFile.lba48.lba_24_31 = (address >> 24) & 0xff;
fTaskFile.lba48.lba_32_39 = (address >> 32) & 0xff;
fTaskFile.lba48.lba_40_47 = (address >> 40) & 0xff;
fTaskFile.lba48.command = s48BitCommands[request->UseDMA()
? 1 : 0][request->IsWrite() ? 1 : 0];
} else {
// normal LBA
if (sectorCount > 0x100) {
TRACE_ERROR("invalid sector count %lu\n", sectorCount);
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}
fRegisterMask = ATA_MASK_SECTOR_COUNT
| ATA_MASK_LBA_LOW
| ATA_MASK_LBA_MID
| ATA_MASK_LBA_HIGH
| ATA_MASK_DEVICE_HEAD;
fTaskFile.lba.sector_count = sectorCount & 0xff;
fTaskFile.lba.lba_0_7 = address & 0xff;
fTaskFile.lba.lba_8_15 = (address >> 8) & 0xff;
fTaskFile.lba.lba_16_23 = (address >> 16) & 0xff;
fTaskFile.lba.lba_24_27 = (address >> 24) & 0xf;
fTaskFile.lba.command = s28BitCommands[request->UseDMA()
? 1 : 0][request->IsWrite() ? 1 : 0];
}
} else {
// CHS mode - we do not support it anymore
TRACE_ERROR("chs mode not supported\n");
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}
return B_OK;
}
status_t
ATADevice::_PrepareDMA(ATARequest *request)
{
return B_ERROR;
}
status_t
ATADevice::_PreparePIO(ATARequest *request)
{
request->PrepareSGInfo();
return B_OK;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "ATAPrivate.h"
#include <vm.h>
#include <string.h>
/*! Copy data between ccb data and buffer
ccb - ccb to copy data from/to
offset - offset of data in ccb
allocation_length- limit of ccb's data buffer according to CDB
buffer - data to copy data from/to
size - number of bytes to copy
to_buffer - true: copy from ccb to buffer
false: copy from buffer to ccb
return: true, if data of ccb was large enough
*/
bool
copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength,
void *buffer, int size, bool toBuffer)
{
const physical_entry *sgList = ccb->sg_list;
int sgCount = ccb->sg_count;
// skip unused S/G entries
while (sgCount > 0 && offset >= sgList->size) {
offset -= sgList->size;
++sgList;
--sgCount;
}
if (sgCount == 0)
return false;
// remaining bytes we are allowed to copy from/to ccb
int requestSize = MIN(allocationLength, ccb->data_length) - offset;
// copy one S/G entry at a time
for (; size > 0 && requestSize > 0 && sgCount > 0; ++sgList, --sgCount) {
size_t bytes;
bytes = MIN(size, requestSize);
bytes = MIN(bytes, sgList->size);
if (toBuffer) {
vm_memcpy_from_physical(buffer, (addr_t)sgList->address + offset,
bytes, false);
} else {
vm_memcpy_to_physical((addr_t)sgList->address + offset, buffer,
bytes, false);
}
buffer = (char *)buffer + bytes;
size -= bytes;
offset = 0;
}
return size == 0;
}

View File

@ -0,0 +1,279 @@
#include "ATAPrivate.h"
scsi_for_sim_interface *gSCSIModule = NULL;
device_manager_info *gDeviceManager = NULL;
static status_t
ata_sim_init_bus(device_node *node, void **cookie)
{
ATAChannel *channel = new(std::nothrow) ATAChannel(node);
if (channel == NULL)
return B_NO_MEMORY;
status_t result = channel->InitCheck();
if (result != B_OK) {
TRACE_ERROR("failed to set up ata channel object\n");
return result;
}
*cookie = channel;
return B_OK;
}
static void
ata_sim_uninit_bus(void *cookie)
{
ATAChannel *channel = (ATAChannel *)cookie;
delete channel;
}
static void
ata_sim_bus_removed(void *cookie)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() != NULL) {
gSCSIModule->block_bus(channel->Bus());
channel->SetBus(NULL);
}
}
static void
ata_sim_set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus)
{
ATAChannel *channel = (ATAChannel *)cookie;
channel->SetBus(bus);
channel->ScanBus();
}
static void
ata_sim_scsi_io(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL) {
ccb->subsys_status = SCSI_NO_HBA;
gSCSIModule->finished(ccb, 1);
return;
}
if (channel->ExecuteIO(ccb) == B_BUSY)
gSCSIModule->requeue(ccb, true);
}
static uchar
ata_sim_abort(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL)
return SCSI_NO_HBA;
// aborting individual commands is not possible
return SCSI_REQ_CMP;
}
static uchar
ata_sim_reset_device(scsi_sim_cookie cookie, uchar targetId, uchar targetLun)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL)
return SCSI_NO_HBA;
// TODO: implement
return SCSI_REQ_INVALID;
}
static uchar
ata_sim_term_io(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL)
return SCSI_NO_HBA;
// we don't terminate commands, ignore
return SCSI_REQ_CMP;
}
static uchar
ata_sim_path_inquiry(scsi_sim_cookie cookie, scsi_path_inquiry *info)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL)
return SCSI_NO_HBA;
channel->PathInquiry(info);
return SCSI_REQ_CMP;
}
static uchar
ata_sim_rescan_bus(scsi_sim_cookie cookie)
{
// TODO: implement
return SCSI_REQ_CMP;
}
static uchar
ata_sim_reset_bus(scsi_sim_cookie cookie)
{
ATAChannel *channel = (ATAChannel *)cookie;
if (channel->Bus() == NULL)
return SCSI_NO_HBA;
channel->Reset(NULL, NULL);
return SCSI_REQ_CMP;
}
static void
ata_sim_get_restrictions(scsi_sim_cookie cookie, uchar targetID,
bool *isATAPI, bool *noAutoSense, uint32 *maxBlocks)
{
ATAChannel *channel = (ATAChannel *)cookie;
channel->GetRestrictions(targetID, isATAPI, noAutoSense, maxBlocks);
}
static status_t
ata_sim_control(scsi_sim_cookie cookie, uchar targetID, uint32 op, void *buffer,
size_t length)
{
// TODO implement
return B_BAD_VALUE;
}
status_t
ata_channel_added(device_node *parent)
{
const char *controllerName;
if (gDeviceManager->get_attr_string(parent,
ATA_CONTROLLER_CONTROLLER_NAME_ITEM, &controllerName, true) != B_OK) {
TRACE_ERROR("controller name missing\n");
return B_ERROR;
}
uint32 channelID = gDeviceManager->create_id(ATA_CHANNEL_ID_GENERATOR);
if (channelID < 0) {
TRACE_ERROR("out of channel ids\n");
return B_ERROR;
}
device_attr attributes[] =
{
{
B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
{ string: SCSI_FOR_SIM_MODULE_NAME }
},
{
SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE,
{ string: controllerName }
},
// 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
// to fix specific drive bugs use ATAChannel::GetRestrictions()
{ B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { ui32: 255 } },
{ ATA_CHANNEL_ID_ITEM, B_UINT32_TYPE, { ui32: channelID } },
{ NULL }
};
return gDeviceManager->register_node(parent, ATA_SIM_MODULE_NAME,
attributes, NULL, NULL);
}
status_t
ata_interrupt_handler(void *cookie, uint8 status)
{
return B_UNHANDLED_INTERRUPT;
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
break;
}
return B_ERROR;
}
scsi_sim_interface ata_sim_module = {
{
{
ATA_SIM_MODULE_NAME,
0,
std_ops
},
NULL, // supported devices
NULL, // register node
ata_sim_init_bus,
ata_sim_uninit_bus,
NULL, // register child devices
NULL, // rescan
ata_sim_bus_removed,
NULL, // suspend
NULL, // resume
},
ata_sim_set_scsi_bus,
ata_sim_scsi_io,
ata_sim_abort,
ata_sim_reset_device,
ata_sim_term_io,
ata_sim_path_inquiry,
ata_sim_rescan_bus,
ata_sim_reset_bus,
ata_sim_get_restrictions,
ata_sim_control
};
ata_for_controller_interface ata_for_controller_module = {
{
{
ATA_FOR_CONTROLLER_MODULE_NAME,
0,
&std_ops
},
NULL, // supported devices
ata_channel_added,
NULL,
NULL,
NULL
},
ata_interrupt_handler
};
module_dependency module_dependencies[] = {
{ SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSIModule },
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
{}
};
module_info *modules[] = {
(module_info *)&ata_for_controller_module,
(module_info *)&ata_sim_module,
NULL
};

View File

@ -0,0 +1,21 @@
#include "ATAPrivate.h"
ATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index)
: ATADevice(channel, index),
fLastLun(1)
{
}
ATAPIDevice::~ATAPIDevice()
{
}
status_t
ATAPIDevice::ExecuteIO(ATARequest *request)
{
request->SetStatus(SCSI_SEL_TIMEOUT);
request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
return B_ERROR;
}

View File

@ -0,0 +1,279 @@
#ifndef ATA_PRIVATE_H
#define ATA_PRIVATE_H
#include <ata_types.h>
#include <bus/ATA.h>
#include <bus/SCSI.h>
#include <device_manager.h>
#include <lock.h>
#include <new>
#include <safemode.h>
#include <scsi_cmds.h>
#include <stdio.h>
#include <string.h>
#include <util/AutoLock.h>
#include "ATACommands.h"
#include "ATATracing.h"
#include "ata_device_infoblock.h"
#define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000
#define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000
#define ATA_SIGNATURE_ATAPI 0xeb140101
#define ATA_SIM_MODULE_NAME "bus_managers/ata/sim/driver_v1"
#define ATA_CHANNEL_ID_GENERATOR "ata/channel_id"
#define ATA_CHANNEL_ID_ITEM "ata/channel_id"
enum {
ATA_DEVICE_READY_REQUIRED = 0x01,
ATA_IS_WRITE = 0x02,
ATA_DMA_TRANSFER = 0x03,
ATA_CHECK_ERROR_BIT = 0x04,
ATA_WAIT_FINISH = 0x08
};
class ATADevice;
class ATARequest;
extern scsi_for_sim_interface *gSCSIModule;
extern device_manager_info *gDeviceManager;
bool copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength,
void *buffer, int size, bool toBuffer);
class ATAChannel {
public:
ATAChannel(device_node *node);
~ATAChannel();
status_t InitCheck();
uint32 ChannelID() { return fChannelID; };
// SCSI stuff
void SetBus(scsi_bus bus);
scsi_bus Bus() { return fSCSIBus; };
status_t ScanBus();
void PathInquiry(scsi_path_inquiry *info);
void GetRestrictions(uint8 targetID,
bool *isATAPI, bool *noAutoSense,
uint32 *maxBlocks);
status_t ExecuteIO(scsi_ccb *ccb);
// ATA stuff
status_t SelectDevice(uint8 index);
bool IsDevicePresent(uint8 index);
status_t Reset(bool *presence, uint32 *signatures);
bool UseDMA() { return fUseDMA; };
status_t Wait(uint8 setBits, uint8 clearedBits,
uint32 flags, bigtime_t timeout);
status_t WaitDataRequest(bool high);
status_t WaitDeviceReady();
status_t WaitForIdle();
// request handling
status_t SendRequest(ATARequest *request,
uint32 flags);
status_t FinishRequest(ATARequest *request,
uint32 flags, uint8 errorMask);
// data transfers
status_t ExecuteDMATransfer(ATARequest *request);
status_t ExecutePIOTransfer(ATARequest *request);
status_t ReadPIO(uint8 *buffer, size_t length);
private:
status_t _ReadRegs(ata_task_file *taskFile,
ata_reg_mask mask);
status_t _WriteRegs(ata_task_file *taskFile,
ata_reg_mask mask);
status_t _WriteControl(uint8 value);
uint8 _AltStatus();
void _FlushAndWait(bigtime_t waitTime);
status_t _ReadPIOBlock(ATARequest *request,
size_t length);
status_t _WritePIOBlock(ATARequest *request,
size_t length);
status_t _TransferPIOBlock(ATARequest *request,
size_t length, size_t *transferred);
status_t _TransferPIOPhysical(ATARequest *request,
addr_t physicalAddress, size_t length,
size_t *transferred);
status_t _TransferPIOVirtual(ATARequest *request,
uint8 *virtualAddress, size_t length,
size_t *transferred);
const char * _DebugContext() { return fDebugContext; };
device_node * fNode;
uint32 fChannelID;
ata_controller_interface * fController;
void * fCookie;
mutex fLock;
status_t fStatus;
scsi_bus fSCSIBus;
uint8 fDeviceCount;
ATADevice ** fDevices;
bool fUseDMA;
ATARequest * fRequest;
char fDebugContext[16];
};
class ATADevice {
public:
ATADevice(ATAChannel *channel,
uint8 index);
virtual ~ATADevice();
// SCSI stuff
status_t ModeSense(ATARequest *request);
status_t TestUnitReady(ATARequest *request);
status_t SynchronizeCache(ATARequest *request);
status_t Eject(ATARequest *request);
status_t Inquiry(ATARequest *request);
status_t ReadCapacity(ATARequest *request);
virtual status_t ExecuteIO(ATARequest *request);
// ATA stuff
virtual bool IsATAPI() { return false; };
bool UseDMA() { return fUseDMA; };
bool UseLBA() { return fUseLBA; };
bool Use48Bits() { return fUse48Bits; };
status_t Select();
ata_task_file * TaskFile() { return &fTaskFile; };
ata_reg_mask RegisterMask() { return fRegisterMask; };
status_t SetFeature(int feature);
status_t DisableCommandQueueing();
status_t ConfigureDMA();
status_t Configure();
status_t Identify();
status_t ExecuteReadWrite(ATARequest *request,
uint64 address, uint32 sectorCount);
private:
status_t _FillTaskFile(ATARequest *request,
uint64 address);
status_t _PrepareDMA(ATARequest *request);
status_t _PreparePIO(ATARequest *request);
const char * _DebugContext() { return fDebugContext; };
ATAChannel * fChannel;
uint8 fIndex;
bool fUseLBA;
bool fUse48Bits;
bool fUseDMA;
uint64 fTotalSectors;
ata_device_infoblock fInfoBlock;
ata_task_file fTaskFile;
ata_reg_mask fRegisterMask;
char fDebugContext[16];
};
class ATAPIDevice : public ATADevice {
public:
ATAPIDevice(ATAChannel *channel,
uint8 index);
virtual ~ATAPIDevice();
virtual status_t ExecuteIO(ATARequest *request);
virtual bool IsATAPI() { return true; };
private:
uint8 fLastLun;
};
class ATARequest {
public:
ATARequest();
void SetStatus(uint8 status);
uint8 Status() { return fStatus; };
void ClearSense();
void SetSense(uint8 key, uint16 codeQualifier);
uint8 SenseKey() { return fSenseKey; };
uint8 SenseCode() { return fSenseCode; };
uint8 SenseQualifier() { return fSenseQualifier; };
void SetDevice(ATADevice *device);
ATADevice * Device() { return fDevice; };
void SetTimeout(bigtime_t timeout);
bigtime_t Timeout() { return fTimeout; };
void SetIsWrite(bool isWrite);
bool IsWrite() { return fIsWrite; };
void SetUseDMA(bool useDMA);
bool UseDMA() { return fUseDMA; };
void SetBlocksLeft(uint32 blocksLeft);
uint32 * BlocksLeft() { return &fBlocksLeft; };
status_t Finish(bool resubmit);
// SCSI stuff
void SetCCB(scsi_ccb *ccb);
scsi_ccb * CCB() { return fCCB; };
void PrepareSGInfo();
void AdvanceSG(uint32 bytes);
uint32 SGElementsLeft()
{ return fSGElementsLeft; };
const physical_entry * CurrentSGElement()
{ return fCurrentSGElement; };
uint32 CurrentSGOffset()
{ return fCurrentSGOffset; };
void SetOddByte(uint8 byte);
bool GetOddByte(uint8 *byte);
void RequestSense();
private:
void _FillSense(scsi_sense *sense);
const char * _DebugContext() { return " request"; };
uint8 fStatus;
uint8 fSenseKey;
uint8 fSenseCode;
uint8 fSenseQualifier;
ATADevice * fDevice;
bigtime_t fTimeout;
uint32 fBlocksLeft;
bool fIsWrite;
bool fUseDMA;
scsi_ccb * fCCB;
uint32 fSGElementsLeft;
const physical_entry * fCurrentSGElement;
uint32 fCurrentSGOffset;
bool fHasOddByte;
uint8 fOddByte;
};
#endif // ATA_PRIVATE_H

View File

@ -0,0 +1,195 @@
#include "ATAPrivate.h"
ATARequest::ATARequest()
: fDevice(NULL),
fTimeout(0),
fBlocksLeft(0),
fIsWrite(false),
fUseDMA(false),
fCCB(NULL)
{
ClearSense();
}
void
ATARequest::SetStatus(uint8 status)
{
fStatus = status;
}
void
ATARequest::SetSense(uint8 key, uint16 codeQualifier)
{
fSenseKey = key;
fSenseCode = (uint8)(codeQualifier >> 8);
fSenseQualifier = (uint8)(codeQualifier & 0xff);
}
void
ATARequest::ClearSense()
{
fSenseKey = fSenseCode = fSenseQualifier = 0;
}
void
ATARequest::SetDevice(ATADevice *device)
{
fDevice = device;
}
void
ATARequest::SetTimeout(bigtime_t timeout)
{
fTimeout = timeout;
}
void
ATARequest::SetIsWrite(bool isWrite)
{
fIsWrite = isWrite;
}
void
ATARequest::SetUseDMA(bool useDMA)
{
fUseDMA = useDMA;
}
void
ATARequest::SetBlocksLeft(uint32 blocksLeft)
{
fBlocksLeft = blocksLeft;
}
status_t
ATARequest::Finish(bool resubmit)
{
// when the request completed and has set sense
// data, report this to the scsi stack by setting
// CHECK CONDITION status
if (fStatus == SCSI_REQ_CMP && fSenseKey != 0) {
TRACE("setting check condition\n");
fCCB->subsys_status = SCSI_REQ_CMP_ERR;
fCCB->device_status = SCSI_STATUS_CHECK_CONDITION;
// copy sense data if caller requested it
if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) {
// we cannot copy sense directly as sense buffer may be too small
scsi_sense sense;
_FillSense(&sense);
size_t senseLength = MIN(sizeof(fCCB->sense), sizeof(sense));
memcpy(fCCB->sense, &sense, senseLength);
fCCB->sense_resid = SCSI_MAX_SENSE_SIZE - senseLength;
fCCB->subsys_status |= SCSI_AUTOSNS_VALID;
ClearSense();
}
} else
fCCB->subsys_status = fStatus;
if (resubmit)
gSCSIModule->resubmit(fCCB);
else
gSCSIModule->finished(fCCB, 1);
return B_OK;
}
void
ATARequest::SetCCB(scsi_ccb *ccb)
{
fCCB = ccb;
fStatus = SCSI_REQ_CMP;
}
void
ATARequest::RequestSense()
{
// Copy sense data from last request into data buffer of current request.
// The sense data of last request is still present in the current request,
// as it isn't cleared on SCSI_OP_REQUEST_SENSE.
scsi_sense sense;
if (fSenseKey != 0)
_FillSense(&sense);
else
memset(&sense, 0, sizeof(sense));
scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
copy_sg_data(fCCB, 0, command->allocation_length, &sense, sizeof(sense),
false);
fCCB->data_resid = fCCB->data_length - MIN(MIN(sizeof(sense),
command->allocation_length), fCCB->data_length);
ClearSense();
}
void
ATARequest::PrepareSGInfo()
{
fSGElementsLeft = fCCB->sg_count;
fCurrentSGElement = fCCB->sg_list;
fCurrentSGOffset = 0;
fHasOddByte = false;
fCCB->data_resid = fCCB->data_length;
}
void
ATARequest::AdvanceSG(uint32 bytes)
{
uint32 bytesLeft = fCurrentSGElement->size - fCurrentSGOffset;
if (bytesLeft <= bytes) {
fCurrentSGOffset = 0;
fCurrentSGElement++;
fSGElementsLeft--;
} else
fCurrentSGOffset += bytes;
}
void
ATARequest::SetOddByte(uint8 byte)
{
fOddByte = byte;
fHasOddByte = true;
}
bool
ATARequest::GetOddByte(uint8 *byte)
{
if (!fHasOddByte)
return false;
if (byte != NULL)
*byte = fOddByte;
fHasOddByte = false;
return true;
}
void
ATARequest::_FillSense(scsi_sense *sense)
{
memset(sense, 0, sizeof(*sense));
sense->error_code = SCSIS_CURR_ERROR;
sense->sense_key = fSenseKey;
sense->add_sense_length = sizeof(*sense) - 7;
sense->asc = fSenseCode;
sense->ascq = fSenseQualifier;
sense->sense_key_spec.raw.SKSV = 0; // no additional info
}

View File

@ -0,0 +1,35 @@
#ifndef ATA_TRACING_H
#define ATA_TRACING_H
//#define TRACE_ATA
#ifdef TRACE_ATA
#define TRACE(x...) { \
dprintf("ata%s: ", _DebugContext()); \
dprintf(x); \
}
#define TRACE_FUNCTION(x...) { \
dprintf("ata%s: %s: ", _DebugContext(), \
__FUNCTION__); \
dprintf(x); \
}
#else
#define TRACE(x...) /* nothing */
#define TRACE_FUNCTION(x...) /* nothing */
#endif
#define TRACE_ALWAYS(x...) { \
dprintf("ata%s: ", _DebugContext()); \
dprintf(x); \
}
#define TRACE_ERROR(x...) { \
dprintf("ata%s error: ", _DebugContext()); \
dprintf(x); \
}
inline const char *
_DebugContext()
{
return "";
}
#endif // ATA_TRACING_H

View File

@ -0,0 +1,187 @@
/*
* Copyright 2004-2006, Haiku, Inc. All RightsReserved.
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#ifndef _ATA_DEVICE_INFOBLOCK_H_
#define _ATA_DEVICE_INFOBLOCK_H_
/*
Definition of response to ATA_COMMAND_IDENTIFY_DEVICE or
ATA_COMMAND_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.
*/
#include <lendian_bitfield.h>
#define ATA_GET_INFO_BLOCK 0x2710
#define ATA_GET_STATUS 0x2711
// 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)
} ata_device_infoblock;
#endif /* _ATA_DEVICE_INFOBLOCK_H_ */