/* $Id: VSCSIInternal.h $ */
/** @file
 * Virtual SCSI driver: Internal defines
 */

/*
 * Copyright (C) 2006-2010 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */
#ifndef ___VSCSIInternal_h
#define ___VSCSIInternal_h

#include <VBox/vscsi.h>
#include <VBox/scsi.h>
#include <iprt/memcache.h>

#include "VSCSIInline.h"

/** Pointer to an internal virtual SCSI device. */
typedef VSCSIDEVICEINT        *PVSCSIDEVICEINT;
/** Pointer to an internal virtual SCSI device LUN. */
typedef VSCSILUNINT           *PVSCSILUNINT;
/** Pointer to an internal virtual SCSI device LUN pointer. */
typedef PVSCSILUNINT          *PPVSCSILUNINT;
/** Pointer to a virtual SCSI LUN descriptor. */
typedef struct VSCSILUNDESC   *PVSCSILUNDESC;
/** Pointer to a virtual SCSI request. */
typedef VSCSIREQINT           *PVSCSIREQINT;
/** Pointer to a virtual SCSI I/O request. */
typedef VSCSIIOREQINT         *PVSCSIIOREQINT;

/**
 * Virtual SCSI device.
 */
typedef struct VSCSIDEVICEINT
{
    /** Request completion callback */
    PFNVSCSIREQCOMPLETED pfnVScsiReqCompleted;
    /** Opaque user data. */
    void                *pvVScsiDeviceUser;
    /** Number of LUNs currently attached. */
    uint32_t             cLunsAttached;
    /** How many LUNs are fitting in the array. */
    uint32_t             cLunsMax;
    /** Request cache */
    RTMEMCACHE           hCacheReq;
    /** Pointer to the array of LUN handles.
     *  The index is the LUN id. */
    PPVSCSILUNINT        papVScsiLun;
} VSCSIDEVICEINT;

/**
 * Virtual SCSI device LUN.
 */
typedef struct VSCSILUNINT
{
    /** Pointer to the parent SCSI device. */
    PVSCSIDEVICEINT      pVScsiDevice;
    /** Opaque user data */
    void                *pvVScsiLunUser;
    /** I/O callback table */
    PVSCSILUNIOCALLBACKS pVScsiLunIoCallbacks;
    /** Pointer to the LUN type descriptor. */
    PVSCSILUNDESC        pVScsiLunDesc;
    /** I/O request processing data */
    struct
    {
        /** Number of outstanding tasks on this LUN. */
        volatile uint32_t cReqOutstanding;
    } IoReq;
} VSCSILUNINT;

/**
 * VSCSI Scatter/gather list handling
 */
typedef struct VSCSIIOMEMCTX
{
    /** Pointer to the scatter/gather list. */
    PCRTSGSEG      paDataSeg;
    /** Number of segments. */
    size_t         cSegments;
    /** Current segment we are in. */
    unsigned       iSegIdx;
    /** Pointer to the current buffer. */
    uint8_t       *pbBuf;
    /** Number of bytes left in the current buffer. */
    size_t         cbBufLeft;
} VSCSIIOMEMCTX;
/** Pointer to a I/O memory context. */
typedef VSCSIIOMEMCTX *PVSCSIIOMEMCTX;

/**
 * Virtual SCSI request.
 */
typedef struct VSCSIREQINT
{
    /** The LUN the request is for. */
    uint32_t             iLun;
    /** The CDB */
    uint8_t             *pbCDB;
    /** Size of the CDB */
    size_t               cbCDB;
    /** I/O memory context */
    VSCSIIOMEMCTX        IoMemCtx;
    /** Pointer to the sense buffer. */
    uint8_t             *pbSense;
    /** Size of the sense buffer */
    size_t               cbSense;
    /** Opaque user data associated with this request */
    void                *pvVScsiReqUser;
} VSCSIREQINT;

/**
 * Virtual SCSI I/O request.
 */
typedef struct VSCSIIOREQINT
{
    /** The associated request. */
    PVSCSIREQINT        pVScsiReq;
    /** Lun for this I/O request. */
    PVSCSILUNINT        pVScsiLun;
    /** Transfer direction */
    VSCSIIOREQTXDIR     enmTxDir;
    /** Start offset */
    uint64_t            uOffset;
    /** Number of bytes to transfer */
    size_t              cbTransfer;
    /** Number of bytes the S/G list holds */
    size_t              cbSeg;
    /** Number of segments. */
    unsigned            cSeg;
    /** Segment array. */
    PCRTSGSEG           paSeg;
} VSCSIIOREQINT;

/**
 * Virtual SCSI LUN descriptor.
 */
typedef struct VSCSILUNDESC
{
    /** Device type this descriptor emulates. */
    VSCSILUNTYPE         enmLunType;
    /** Descriptor name */
    const char          *pcszDescName;
    /** LUN type size */
    size_t               cbLun;

    /**
     * Initialise a Lun instance.
     *
     * @returns VBox status code.
     * @param   pVScsiLun    The SCSI LUN instance.
     */
    DECLR3CALLBACKMEMBER(int, pfnVScsiLunInit, (PVSCSILUNINT pVScsiLun));

    /**
     * Destroy a Lun instance.
     *
     * @returns VBox status code.
     * @param   pVScsiLun    The SCSI LUN instance.
     */
    DECLR3CALLBACKMEMBER(int, pfnVScsiLunDestroy, (PVSCSILUNINT pVScsiLun));

    /**
     * Processes a SCSI request.
     *
     * @returns VBox status code.
     * @param   pVScsiLun    The SCSI LUN instance.
     * @param   pVScsiReq    The SCSi request to process.
     */
    DECLR3CALLBACKMEMBER(int, pfnVScsiLunReqProcess, (PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq));

} VSCSILUNDESC;

/** Maximum number of LUNs a device can have. */
#define VSCSI_DEVICE_LUN_MAX 128

/**
 * Completesa SCSI request and calls the completion handler.
 *
 * @returns nothing.
 * @param   pVScsiDevice    The virtual SCSI device.
 * @param   pVScsiReq       The request which completed.
 * @param   rcReq           The status code
 *                          One of the SCSI_STATUS_* #defines.
 */
void vscsiDeviceReqComplete(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
                            int rcReq);

/**
 * Initialize a I/O memory context.
 *
 * @returns nothing
 * @param   pIoMemCtx    Pointer to a unitialized I/O memory context.
 * @param   paDataSeg    Pointer to the S/G list.
 * @param   cSegments    Number of segments in the S/G list.
 */
void vscsiIoMemCtxInit(PVSCSIIOMEMCTX pIoMemCtx, PCRTSGSEG paDataSeg, size_t cSegments);

/**
 * Return a buffer from the I/O memory context.
 *
 * @returns Pointer to the buffer
 * @param   pIoMemCtx    Pointer to the I/O memory context.
 * @param   pcbData      Pointer to the amount of byte requested.
 *                       If the current buffer doesn't have enough bytes left
 *                       the amount is returned in the variable.
 */
uint8_t *vscsiIoMemCtxGetBuffer(PVSCSIIOMEMCTX pIoMemCtx, size_t *pcbData);

/**
 * Copies data to a buffer described by a I/O memory context.
 *
 * @returns The amount of data copied before we run out of either
 *          I/O memory or src data.
 * @param   pIoMemCtx    The I/O memory context to copy the data into.
 * @param   pbData       Pointer to the data data to copy.
 * @param   cbData       Amount of data to copy.
 */
size_t vscsiCopyToIoMemCtx(PVSCSIIOMEMCTX pIoMemCtx, uint8_t *pbData, size_t cbData);

/**
 * Copies data from a buffer described by a I/O memory context.
 *
 * @returns The amount of data copied before we run out of either
 *          I/O memory or dst data.
 * @param   pIoMemCtx    The I/O memory context to copy the data from.
 * @param   pbData       Pointer to the destination buffer.
 * @param   cbData       Amount of data to copy.
 */
size_t vscsiCopyFromIoMemCtx(PVSCSIIOMEMCTX pIoMemCtx, uint8_t *pbData, size_t cbData);

/**
 * Sets a ok sense code.
 *
 * @returns SCSI status code.
 * @param   pVScsiReq    The SCSI request.
 */
int vscsiReqSenseOkSet(PVSCSIREQINT pVScsiReq);

/**
 * Sets a error sense code.
 *
 * @returns SCSI status code.
 * @param   pVScsiReq     The SCSI request.
 * @param   uSCSISenseKey The SCSi sense key to set.
 * @param   uSCSIASC      The ASC value.
 */
int vscsiReqSenseErrorSet(PVSCSIREQINT pVScsiReq, uint8_t uSCSISenseKey, uint8_t uSCSIASC);

/**
 * Enqueues a new flush request
 *
 * @retruns VBox status code.
 * @param   pVScsiLun    The LUN instance which issued the request.
 * @param   pVScsiReq    The virtual SCSI request associated with the flush.
 */
int vscsiIoReqFlushEnqueue(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq);

/**
 * Enqueue a new data transfer request.
 *
 * @returns VBox status code.
 * @param   pVScsiLun   The LUN instance which issued the request.
 * @param   pVScsiReq   The virtual SCSI request associated with the transfer.
 * @param   enmTxDir    Transfer direction.
 * @param   uOffset     Start offset of the transfer.
 * @param   cbTransfer  Number of bytes to transfer.
 */
int vscsiIoReqTransferEnqueue(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq,
                              VSCSIIOREQTXDIR enmTxDir, uint64_t uOffset,
                              size_t cbTransfer);

/**
 * Returns the current number of outstanding tasks on the given LUN.
 *
 * @returns Number of outstanding tasks.
 * @param   pVScsiLun   The LUN to check.
 */
uint32_t vscsiIoReqOutstandingCountGet(PVSCSILUNINT pVScsiLun);

DECLINLINE(int) vscsiLunMediumGetSize(PVSCSILUNINT pVScsiLun, uint64_t *pcbSize)
{
    return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunMediumGetSize(pVScsiLun,
                                                                     pVScsiLun->pvVScsiLunUser,
                                                                     pcbSize);
}

DECLINLINE(int) vscsiLunReqTransferEnqueue(PVSCSILUNINT pVScsiLun, PVSCSIIOREQINT pVScsiIoReq)
{
    return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunReqTransferEnqueue(pVScsiLun,
                                                                          pVScsiLun->pvVScsiLunUser,
                                                                          pVScsiIoReq);
}

#endif /* ___VSCSIInternal_h */

