* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* Copyright 2008, Marcus Overhagen.
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2002-2003, Thomas Kurschel.
*
* Distributed under the terms of the MIT License.
*/
#include "ATAPrivate.h"
ATAChannel::ATAChannel(device_node *node)
:
fNode(node),
fChannelID(0),
fController(NULL),
fCookie(NULL),
fExpectsInterrupt(false),
fStatus(B_NO_INIT),
fSCSIBus(NULL),
fDeviceCount(0),
fDevices(NULL),
fUseDMA(true),
fRequest(NULL)
{
B_INITIALIZE_SPINLOCK(&fInterruptLock);
fInterruptCondition.Init(this, "ata dma transfer");
gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID,
true);
snprintf(fDebugContext, sizeof(fDebugContext), " %" B_PRIu32, fChannelID);
if (fUseDMA) {
if (get_safemode_boolean(B_SAFEMODE_DISABLE_IDE_DMA, false)) {
TRACE_ALWAYS("disabling DMA because of safemode setting\n");
fUseDMA = false;
}
}
if (fUseDMA) {
uint8 canDMA;
if (gDeviceManager->get_attr_uint8(node, ATA_CONTROLLER_CAN_DMA_ITEM,
&canDMA, true) != B_OK) {
TRACE_ERROR("unknown if controller supports DMA, not using it\n");
fUseDMA = false;
}
if (canDMA == 0) {
TRACE_ALWAYS("controller doesn't support DMA, disabling\n");
fUseDMA = false;
}
}
fRequest = new(std::nothrow) ATARequest(true);
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;
device_node *parent = gDeviceManager->get_parent_node(node);
fStatus = gDeviceManager->get_driver(parent,
(driver_module_info **)&fController, &fCookie);
gDeviceManager->put_node(parent);
fController->set_channel(fCookie, this);
}
ATAChannel::~ATAChannel()
{
if (fDevices) {
for (uint8 i = 0; i < fDeviceCount; i++)
delete fDevices[i];
delete [] fDevices;
}
delete fRequest;
}
status_t
ATAChannel::InitCheck()
{
return fStatus;
}
void
ATAChannel::SetBus(scsi_bus bus)
{
fSCSIBus = bus;
}
bool
ATAChannel::_DevicePresent(int device)
{
SelectDevice(device);
if (SelectedDevice() != device) {
TRACE_ALWAYS("_DevicePresent: device selection failed for device %i\n",
device);
return false;
}
ata_task_file taskFile;
taskFile.chs.sector_count = 0x5a;
taskFile.chs.sector_number = 0xa5;
if (_WriteRegs(&taskFile, ATA_MASK_SECTOR_COUNT
| ATA_MASK_SECTOR_NUMBER) != B_OK) {
TRACE_ERROR("_DevicePresent: writing registers failed\n");
return false;
}
if (_ReadRegs(&taskFile, ATA_MASK_SECTOR_COUNT
| ATA_MASK_SECTOR_NUMBER) != B_OK) {
TRACE_ERROR("_DevicePresent: reading registers failed\n");
return false;
}
bool present = (taskFile.chs.sector_count == 0x5a &&
taskFile.chs.sector_number == 0xa5);
TRACE_ALWAYS("_DevicePresent: device %i, presence %d\n", device, present);
return present;
}
status_t
ATAChannel::ScanBus()
{
uint deviceMask = 0;
for (int i = 0; i < fDeviceCount; i++)
deviceMask |= (int)_DevicePresent(i) << i;
status_t result = Reset();
if (result != B_OK) {
TRACE_ERROR("resetting the channel failed\n");
return result;
}
TRACE_ALWAYS("deviceMask %d\n", deviceMask);
for (int i = 0; i < fDeviceCount; i++) {
if (!(deviceMask & (1 << i))) {
TRACE_ALWAYS("ignoring device %d\n", i);
continue;
}
TRACE_ALWAYS("probing device %d\n", i);
SelectDevice(i);
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
if (Wait(0, ATA_STATUS_BUSY, 0, 3 * 1000 * 1000) != B_OK) {
uint8 status = AltStatus();
if (status == 0xff || status == 0x7f) {
TRACE_ALWAYS("illegal status value 0x%02x for device %d\n",
status, i);
continue;
} else {
TRACE_ALWAYS("device %d is slow\n", i);
}
}
if (Wait(0, ATA_STATUS_BUSY, 0, 28 * 1000 * 1000) != B_OK) {
TRACE_ALWAYS("device %d reset timeout\n", i);
continue;
}
SelectDevice(i);
WaitForIdle();
if (SelectedDevice() != i) {
TRACE_ALWAYS("device selection failed for device %i\n", i);
continue;
}
ata_task_file taskFile;
if (_ReadRegs(&taskFile, ATA_MASK_LBA_MID | ATA_MASK_LBA_HIGH
| ATA_MASK_ERROR) != B_OK) {
TRACE_ERROR("reading status failed\n");
return B_ERROR;
}
if ((i == 0) && (taskFile.read.error & 0x80)) {
TRACE_ERROR("device 0 indicates that device 1 failed"
" error code is 0x%02x\n", taskFile.read.error);
} else if (taskFile.read.error != 0x01) {
TRACE_ERROR("device %d failed, error code is 0x%02x\n",
i, taskFile.read.error);
}
uint16 signature = taskFile.lba.lba_8_15
| (((uint16)taskFile.lba.lba_16_23) << 8);
TRACE_ALWAYS("signature of device %d: 0x%04x\n", i, signature);
ATADevice *device = NULL;
if (signature == ATA_SIGNATURE_ATAPI)
device = new(std::nothrow) ATAPIDevice(this, i);
else
device = new(std::nothrow) ATADevice(this, i);
if (device == NULL) {
TRACE_ERROR("out of memory allocating device\n");
return B_NO_MEMORY;
}
TRACE("trying ATA%s device %u\n", device->IsATAPI() ? "PI" : "", i);
if (device->Identify() != B_OK) {
delete device;
continue;
}
if (device->Configure() != B_OK) {
TRACE_ERROR("failed to configure device\n");
delete device;
continue;
}
TRACE_ALWAYS("identified ATA%s device %u\n", device->IsATAPI()
? "PI" : "", i);
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->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)
{
*isATAPI = true;
*noAutoSense = false;
*maxBlocks = 0x100;
if (targetID < fDeviceCount && fDevices[targetID] != NULL)
fDevices[targetID]->GetRestrictions(noAutoSense, maxBlocks);
}
status_t
ATAChannel::ExecuteIO(scsi_ccb *ccb)
{
TRACE_FUNCTION("%p\n", ccb);
status_t result = fRequest->Start(ccb);
if (result != B_OK)
return result;
if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE && fRequest->HasSense()) {
TRACE("request sense\n");
fRequest->RequestSense();
fRequest->Finish(false);
return B_OK;
}
fRequest->ClearSense();
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);
result = device->ExecuteIO(fRequest);
fRequest->Finish(false);
return result;
}
status_t
ATAChannel::Control(uint8 targetID, uint32 opcode, void *buffer, size_t length)
{
if (targetID < fDeviceCount && fDevices[targetID] != NULL)
return fDevices[targetID]->Control(opcode, buffer, length);
return B_BAD_VALUE;
}
status_t
ATAChannel::SelectDevice(uint8 device)
{
TRACE_FUNCTION("device: %u\n", device);
if (device > 1)
return B_BAD_INDEX;
ata_task_file taskFile;
taskFile.lba.lba_24_27 = 0;
taskFile.lba.mode = ATA_MODE_LBA;
taskFile.lba.device = device;
status_t result = _WriteRegs(&taskFile, ATA_MASK_DEVICE_HEAD);
if (result != B_OK) {
TRACE_ERROR("writing register failed when trying to select device %d\n",
device);
return result;
}
_FlushAndWait(1);
return B_OK;
}
uint8
ATAChannel::SelectedDevice()
{
ata_task_file taskFile;
if (_ReadRegs(&taskFile, ATA_MASK_DEVICE_HEAD) != B_OK) {
TRACE_ERROR("reading register failed when detecting selected device\n");
return 234;
}
return taskFile.lba.device;
}
status_t
ATAChannel::Reset()
{
TRACE_FUNCTION("\n");
SelectDevice(0);
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);
if (_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS) != B_OK) {
TRACE_ERROR("failed to clear reset signaling\n");
return B_ERROR;
}
_FlushAndWait(150 * 1000);
_Status();
return B_OK;
}
status_t
ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags,
bigtime_t timeout)
{
bigtime_t startTime = system_time();
_FlushAndWait(1);
TRACE("wait for set bits 0x%02x and cleared bits 0x%02x\n",
setBits, clearedBits);
#if ATA_TRACING
unsigned lastStatus = 0x100;
#endif
while (true) {
uint8 status = AltStatus();
if ((flags & ATA_CHECK_ERROR_BIT) != 0
&& (status & ATA_STATUS_BUSY) == 0
&& (status & ATA_STATUS_ERROR) != 0) {
TRACE("wait failed, error bit set, status 0x%02x\n", status);
return B_ERROR;
}
if ((flags & ATA_CHECK_DEVICE_FAULT) != 0
&& (status & ATA_STATUS_BUSY) == 0
&& (status & ATA_STATUS_DEVICE_FAULT) != 0) {
TRACE("wait failed, device fault bit set, status 0x%02x\n", status);
return B_ERROR;
}
if ((status & clearedBits) == 0) {
if ((flags & ATA_WAIT_ANY_BIT) != 0 && (status & setBits) != 0) {
TRACE("wait success, status 0x%02x\n", status);
return B_OK;
}
if ((status & setBits) == setBits) {
TRACE("wait success, status 0x%02x\n", status);
return B_OK;
}
}
bigtime_t elapsedTime = system_time() - startTime;
#if ATA_TRACING
if (lastStatus != status) {
TRACE("wait status changed after %lld, status 0x%02x\n",
elapsedTime, status);
lastStatus = status;
}
#endif
if (elapsedTime > timeout) {
TRACE("wait timeout after %lld, status 0x%02x\n",
elapsedTime, status);
return B_TIMED_OUT;
}
if (elapsedTime < 1000)
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, 0, (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, 50 * 1000);
}
status_t
ATAChannel::Interrupt(uint8 status)
{
SpinLocker locker(fInterruptLock);
if (!fExpectsInterrupt) {
TRACE("interrupt when not expecting transfer\n");
return B_UNHANDLED_INTERRUPT;
}
if ((status & ATA_STATUS_BUSY) != 0) {
TRACE("interrupt while device is busy\n");
return B_UNHANDLED_INTERRUPT;
}
TRACE("interrupt\n");
fInterruptCondition.NotifyAll();
return B_INVOKE_SCHEDULER;
}
void
ATAChannel::PrepareWaitingForInterrupt()
{
TRACE_FUNCTION("\n");
InterruptsSpinLocker locker(fInterruptLock);
fExpectsInterrupt = true;
fInterruptCondition.Add(&fInterruptConditionEntry);
}
status_t
ATAChannel::WaitForInterrupt(bigtime_t timeout)
{
TRACE_FUNCTION("timeout: %lld\n", timeout);
status_t result = fInterruptConditionEntry.Wait(B_RELATIVE_TIMEOUT,
timeout);
InterruptsSpinLocker locker(fInterruptLock);
fExpectsInterrupt = false;
locker.Unlock();
if (result != B_OK) {
TRACE_ERROR("timeout waiting for interrupt\n");
result = RecoverLostInterrupt();
}
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
if (result != B_OK) {
return B_TIMED_OUT;
}
return B_OK;
}
status_t
ATAChannel::RecoverLostInterrupt()
{
uint8 status = _Status();
if (status & (ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST)) {
TRACE_ERROR("RecoverLostInterrupt: device busy, status 0x%02x\n", status);
return B_ERROR;
}
TRACE_ERROR("RecoverLostInterrupt: lost interrupt, status 0x%02x\n", status);
return B_OK;
}
status_t
ATAChannel::SendRequest(ATARequest *request, uint32 flags)
{
TRACE_FUNCTION("\n");
ATADevice *device = request->Device();
TRACE("SendRequest status 0x%02x\n", AltStatus());
if (request->UseDMA())
_WriteControl(0);
if (device->Select() != B_OK) {
TRACE_ERROR("device selection failed\n");
request->SetStatus(SCSI_SEL_TIMEOUT);
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
return B_TIMED_OUT;
}
if (WaitForIdle() != B_OK) {
TRACE_ERROR("device selection timeout\n");
request->SetStatus(SCSI_SEL_TIMEOUT);
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
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);
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
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);
_WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS);
return B_ERROR;
}
return B_OK;
}
status_t
ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
{
TRACE_FUNCTION("\n");
if (flags & ATA_WAIT_FINISH) {
status_t result = Wait(0, ATA_STATUS_BUSY, flags, request->Timeout());
if (result != B_OK) {
TRACE_ERROR("timeout waiting for request finish\n");
request->SetStatus(SCSI_CMD_TIMEOUT);
return result;
}
}
ata_task_file *taskFile = request->Device()->TaskFile();
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;
}
uint8 checkFlags = ATA_STATUS_ERROR;
if (flags & ATA_CHECK_DEVICE_FAULT)
checkFlags |= ATA_STATUS_DEVICE_FAULT;
if ((taskFile->read.status & checkFlags) == 0)
return B_OK;
if ((taskFile->read.error & ATA_ERROR_MEDIUM_CHANGED)
!= ATA_ERROR_MEDIUM_CHANGED) {
TRACE_ERROR("command failed, error bit is set. status 0x%02x, error 0x%02x\n",
taskFile->read.status, taskFile->read.error);
}
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 (request->IsWrite()) {
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) {
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;
}
request->SetSense(SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE);
return B_ERROR;
}
status_t
ATAChannel::PrepareDMA(ATARequest *request)
{
scsi_ccb *ccb = request->CCB();
return fController->prepare_dma(fCookie, ccb->sg_list, ccb->sg_count,
request->IsWrite());
}
status_t
ATAChannel::StartDMA()
{
return fController->start_dma(fCookie);
}
status_t
ATAChannel::FinishDMA()
{
return fController->finish_dma(fCookie);
}
status_t
ATAChannel::ExecutePIOTransfer(ATARequest *request)
{
bigtime_t timeout = request->Timeout();
status_t result = B_OK;
size_t *bytesLeft = request->BytesLeft();
while (*bytesLeft > 0) {
size_t currentLength = MIN(*bytesLeft, request->Device()->BlockSize());
if (request->IsWrite()) {
result = _WritePIOBlock(request, currentLength);
if (result != B_OK) {
TRACE_ERROR("failed to write pio block\n");
break;
}
} else {
result = _ReadPIOBlock(request, currentLength);
if (result != B_OK) {
TRACE_ERROR("failed to read pio block\n");
break;
}
}
*bytesLeft -= currentLength;
if (*bytesLeft > 0) {
if (Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT,
timeout) != B_OK) {
TRACE_ERROR("timeout waiting for device to request data\n");
result = B_TIMED_OUT;
break;
}
}
}
if (result == B_OK && WaitDataRequest(false) != B_OK) {
TRACE_ERROR("device still expects data transfer\n");
result = B_ERROR;
}
return result;
}
status_t
ATAChannel::ReadRegs(ATADevice *device)
{
return _ReadRegs(device->TaskFile(), device->RegisterMask());
}
uint8
ATAChannel::AltStatus()
{
return fController->get_altstatus(fCookie);
}
status_t
ATAChannel::ReadPIO(uint8 *buffer, size_t length)
{
return fController->read_pio(fCookie, (uint16 *)buffer,
length / sizeof(uint16), false);
}
status_t
ATAChannel::WritePIO(uint8 *buffer, size_t length)
{
return fController->write_pio(fCookie, (uint16 *)buffer,
length / sizeof(uint16), true);
}
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);
}
uint8
ATAChannel::_Status()
{
ata_task_file taskFile;
if (_ReadRegs(&taskFile, ATA_MASK_STATUS) != B_OK)
return 0x01;
return taskFile.read.status;
}
status_t
ATAChannel::_WriteControl(uint8 value)
{
return fController->write_device_control(fCookie, ATA_DEVICE_CONTROL_BIT3
| value);
}
void
ATAChannel::_FlushAndWait(bigtime_t waitTime)
{
AltStatus();
if (waitTime > 100)
snooze(waitTime);
else
spin(waitTime);
}
status_t
ATAChannel::_ReadPIOBlock(ATARequest *request, size_t length)
{
size_t transferred = 0;
status_t result = _TransferPIOBlock(request, length, &transferred);
request->CCB()->data_resid -= transferred;
if (request->GetOddByte(NULL)) {
request->CCB()->data_resid++;
}
if (result != B_BUFFER_OVERFLOW)
return result;
if (transferred >= length)
return B_OK;
TRACE_ERROR("pio read: discarding after %lu bytes\n", transferred);
uint8 buffer[32];
length -= transferred;
while (length > 0) {
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;
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;
}
if (transferred >= length)
return B_OK;
static const uint8 buffer[32] = {};
TRACE_ERROR("pio write: discarding after %lu bytes\n", transferred);
length -= transferred;
while (length > 0) {
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)
{
while (length > 0) {
if (request->SGElementsLeft() == 0) {
return B_BUFFER_OVERFLOW;
}
const physical_entry *entry = request->CurrentSGElement();
uint32 offset = request->CurrentSGOffset();
uint32 currentLength = MIN(entry->size - offset, length);
status_t result = _TransferPIOPhysical(request,
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;
}
#include <vm/vm.h>
#include <thread.h>
status_t
ATAChannel::_TransferPIOPhysical(ATARequest *request, addr_t physicalAddress,
size_t length, size_t *transferred)
{
while (length > 0) {
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);
return B_ERROR;
}
ASSERT(physicalAddress % B_PAGE_SIZE == virtualAddress % B_PAGE_SIZE);
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()) {
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);
virtualAddress += length & ~1;
*transferred += length & ~1;
if ((length & 1) != 0)
request->SetOddByte(*virtualAddress);
} else {
uint8 byte;
if (request->GetOddByte(&byte)) {
*virtualAddress++ = byte;
length--;
}
fController->read_pio(fCookie, (uint16 *)virtualAddress, length / 2,
false);
virtualAddress += length & ~1;
*transferred += length & ~1;
if ((length & 1) != 0) {
uint8 buffer[2];
fController->read_pio(fCookie, (uint16 *)buffer, 1, false);
*virtualAddress = buffer[0];
request->SetOddByte(buffer[1]);
*transferred += 2;
}
}
return B_OK;
}