/*
* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* Distributed under the terms of the MIT License.
*/
#include "ATAPrivate.h"
ATARequest::ATARequest(bool hasLock)
:
fHasLock(hasLock),
fDevice(NULL),
fTimeout(0),
fBytesLeft(0),
fIsWrite(false),
fUseDMA(false),
fCCB(NULL)
{
if (hasLock)
mutex_init(&fLock, "ata request");
ClearSense();
}
ATARequest::~ATARequest()
{
if (fHasLock)
mutex_destroy(&fLock);
}
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::SetBytesLeft(uint32 bytesLeft)
{
fBytesLeft = bytesLeft;
}
status_t
ATARequest::Start(scsi_ccb *ccb)
{
if (mutex_trylock(&fLock) != B_OK)
return B_BUSY;
fCCB = ccb;
fStatus = SCSI_REQ_CMP;
fCCB->device_status = SCSI_STATUS_GOOD;
fIsWrite = false;
return B_OK;
}
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;
mutex_unlock(&fLock);
if (resubmit)
gSCSIModule->resubmit(fCCB);
else
gSCSIModule->finished(fCCB, 1);
return B_OK;
}
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
}
↑ V512 A call of the 'memcpy' function will lead to underflow of the buffer 'fCCB->sense'.