root/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp
/*
 * 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
}