* 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:
parent
b2a271d29f
commit
bf9a383524
70
headers/os/drivers/bus/ATA.h
Normal file
70
headers/os/drivers/bus/ATA.h
Normal 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__ */
|
284
headers/private/drivers/ata_types.h
Normal file
284
headers/private/drivers/ata_types.h
Normal 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__ */
|
888
src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp
Normal file
888
src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp
Normal 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;
|
||||
}
|
50
src/add-ons/kernel/bus_managers/ata/ATACommands.h
Normal file
50
src/add-ons/kernel/bus_managers/ata/ATACommands.h
Normal 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
|
585
src/add-ons/kernel/bus_managers/ata/ATADevice.cpp
Normal file
585
src/add-ons/kernel/bus_managers/ata/ATADevice.cpp
Normal 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;
|
||||
}
|
65
src/add-ons/kernel/bus_managers/ata/ATAHelper.cpp
Normal file
65
src/add-ons/kernel/bus_managers/ata/ATAHelper.cpp
Normal 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;
|
||||
}
|
279
src/add-ons/kernel/bus_managers/ata/ATAModule.cpp
Normal file
279
src/add-ons/kernel/bus_managers/ata/ATAModule.cpp
Normal 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
|
||||
};
|
21
src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp
Normal file
21
src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp
Normal 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;
|
||||
}
|
279
src/add-ons/kernel/bus_managers/ata/ATAPrivate.h
Normal file
279
src/add-ons/kernel/bus_managers/ata/ATAPrivate.h
Normal 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
|
195
src/add-ons/kernel/bus_managers/ata/ATARequest.cpp
Normal file
195
src/add-ons/kernel/bus_managers/ata/ATARequest.cpp
Normal 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
|
||||
}
|
35
src/add-ons/kernel/bus_managers/ata/ATATracing.h
Normal file
35
src/add-ons/kernel/bus_managers/ata/ATATracing.h
Normal 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
|
187
src/add-ons/kernel/bus_managers/ata/ata_device_infoblock.h
Normal file
187
src/add-ons/kernel/bus_managers/ata/ata_device_infoblock.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user