diff --git a/headers/os/drivers/bus/ATA.h b/headers/os/drivers/bus/ATA.h new file mode 100644 index 0000000000..e6cd8aaf81 --- /dev/null +++ b/headers/os/drivers/bus/ATA.h @@ -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 +#include + +// 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__ */ diff --git a/headers/private/drivers/ata_types.h b/headers/private/drivers/ata_types.h new file mode 100644 index 0000000000..1dfccf5c04 --- /dev/null +++ b/headers/private/drivers/ata_types.h @@ -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 +#include + +// 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 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__ */ diff --git a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp new file mode 100644 index 0000000000..d10c4e7eed --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp @@ -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 +#include + +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; +} diff --git a/src/add-ons/kernel/bus_managers/ata/ATACommands.h b/src/add-ons/kernel/bus_managers/ata/ATACommands.h new file mode 100644 index 0000000000..d5e620da52 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATACommands.h @@ -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 diff --git a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp new file mode 100644 index 0000000000..d1f6bbc6fb --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp @@ -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; +} diff --git a/src/add-ons/kernel/bus_managers/ata/ATAHelper.cpp b/src/add-ons/kernel/bus_managers/ata/ATAHelper.cpp new file mode 100644 index 0000000000..ba7be94553 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATAHelper.cpp @@ -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 +#include + + +/*! 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; +} diff --git a/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp new file mode 100644 index 0000000000..92b643be3b --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp @@ -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 +}; diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp new file mode 100644 index 0000000000..6966024cce --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp @@ -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; +} diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h new file mode 100644 index 0000000000..72309835d1 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h @@ -0,0 +1,279 @@ +#ifndef ATA_PRIVATE_H +#define ATA_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp b/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp new file mode 100644 index 0000000000..21b6691f9a --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp @@ -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 +} diff --git a/src/add-ons/kernel/bus_managers/ata/ATATracing.h b/src/add-ons/kernel/bus_managers/ata/ATATracing.h new file mode 100644 index 0000000000..6371ed03dd --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ATATracing.h @@ -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 diff --git a/src/add-ons/kernel/bus_managers/ata/ata_device_infoblock.h b/src/add-ons/kernel/bus_managers/ata/ata_device_infoblock.h new file mode 100644 index 0000000000..eff7b4f5e7 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ata/ata_device_infoblock.h @@ -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 + +#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_ */