* Copyright 2008, Marcus Overhagen. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <string.h>
#include "sata_request.h"
#include "scsi_cmds.h"
#define FIS_TYPE_REGISTER_HOST_TO_DEVICE 0x27
sata_request::sata_request()
:
fCcb(NULL),
fIsATAPI(false),
fCompletionSem(create_sem(0, "sata completion")),
fCompletionStatus(0),
fData(NULL),
fDataSize(0)
{
}
sata_request::sata_request(scsi_ccb* ccb)
:
fCcb(ccb),
fIsATAPI(false),
fCompletionSem(-1),
fCompletionStatus(0),
fData(NULL),
fDataSize(0)
{
}
sata_request::~sata_request()
{
if (fCompletionSem >= 0)
delete_sem(fCompletionSem);
}
void
sata_request::SetData(void* data, size_t dataSize)
{
ASSERT(fCcb == NULL);
fData = data;
fDataSize = dataSize;
}
void
sata_request::SetATACommand(uint8 command)
{
memset(fFis, 0, sizeof(fFis));
fFis[0] = FIS_TYPE_REGISTER_HOST_TO_DEVICE;
fFis[1] = 0x80;
fFis[2] = command;
}
void
sata_request::SetATA28Command(uint8 command, uint32 lba, uint8 sectorCount)
{
SetATACommand(command);
fFis[4] = lba & 0xff;
fFis[5] = (lba >> 8) & 0xff;
fFis[6] = (lba >> 16) & 0xff;
fFis[7] = 0x40 | ((lba >> 24) & 0x0f);
fFis[12] = sectorCount & 0xff;
}
void
sata_request::SetATA48Command(uint8 command, uint64 lba, uint16 sectorCount)
{
SetATACommand(command);
fFis[4] = lba & 0xff;
fFis[5] = (lba >> 8) & 0xff;
fFis[6] = (lba >> 16) & 0xff;
fFis[7] = 0x40;
fFis[8] = (lba >> 24) & 0xff;
fFis[9] = (lba >> 32) & 0xff;
fFis[10] = (lba >> 40) & 0xff;
fFis[12] = sectorCount & 0xff;
fFis[13] = (sectorCount >> 8) & 0xff;
}
void
sata_request::SetFeature(uint16 feature)
{
fFis[3] = (uint8)(feature & 0xff);
fFis[11] = (uint8)(feature >> 8);
}
void
sata_request::SetATAPICommand(size_t transferLength)
{
fIsATAPI = true;
SetATACommand(0xa0);
if (1 ) {
if (transferLength == 0)
transferLength = 2;
else if (transferLength > 0xfffe)
transferLength = 0xfffe;
fFis[5] = transferLength & 0xff;
fFis[6] = (transferLength >> 8) & 0xff;
}
}
void
sata_request::Finish(int tfd, size_t bytesTransfered)
{
if ((tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) != 0) {
uint8 status = tfd & 0xff;
uint8 error = (tfd >> 8) & 0xff;
if (!IsTestUnitReady()) {
dprintf("ahci: sata_request::finish ATA command 0x%02x failed:"
" status 0x%02x, error 0x%02x\n", fFis[2], status, error);
}
}
if (fCcb) {
fCcb->data_resid = fCcb->data_length - bytesTransfered;
fCcb->device_status = SCSI_STATUS_GOOD;
fCcb->subsys_status = SCSI_REQ_CMP;
if (tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) {
fCcb->subsys_status = SCSI_REQ_CMP_ERR;
if (fIsATAPI) {
if (!IsTestUnitReady()) {
dprintf("ahci: sata_request::finish ATAPI packet %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x (len %d)\n",
fCcb->cdb[0], fCcb->cdb[1], fCcb->cdb[2], fCcb->cdb[3],
fCcb->cdb[4], fCcb->cdb[5], fCcb->cdb[6], fCcb->cdb[7],
fCcb->cdb[8], fCcb->cdb[9], fCcb->cdb[10],
fCcb->cdb[11], fCcb->cdb[12], fCcb->cdb[13],
fCcb->cdb[14], fCcb->cdb[15], fCcb->cdb_length);
}
fCcb->device_status = SCSI_STATUS_CHECK_CONDITION;
} else {
// TODO check ABORT bit if this is useful
if ((tfd >> 8) & 0x04) { // ABRT
fCcb->subsys_status = SCSI_REQ_ABORTED;
} else {
fCcb->device_status = SCSI_STATUS_CHECK_CONDITION;
fCcb->subsys_status |= SCSI_AUTOSNS_VALID;
fCcb->sense_resid = 0; //FIXME
scsi_sense *sense = (scsi_sense *)fCcb->sense;
sense->error_code = SCSIS_CURR_ERROR;
sense->sense_key = error >> 4;
sense->asc = 0;
sense->ascq = 0;
}
*/
}
}
gSCSI->finished(fCcb, 1);
delete this;
} else {
fCompletionStatus = tfd;
release_sem(fCompletionSem);
}
}
void
sata_request::Abort()
{
dprintf("ahci: sata_request::abort called for command 0x%02x\n", fFis[2]);
if (fCcb != NULL) {
fCcb->subsys_status = SCSI_REQ_ABORTED;
gSCSI->finished(fCcb, 1);
delete this;
} else {
fCompletionStatus = ATA_STATUS_ERROR;
release_sem(fCompletionSem);
}
}
void
sata_request::WaitForCompletion()
{
ASSERT(fCcb == NULL);
acquire_sem(fCompletionSem);
}
int
sata_request::CompletionStatus()
{
ASSERT(fCcb == NULL);
return fCompletionStatus;
}