/***************************************************************************
 * Copyright 2004 - 2012 VMware, Inc.  All rights reserved.
 ***************************************************************************/

/*
 * @VMKAPIMOD_LICENSE@
 */

/*
 ***********************************************************************
 * SCSI                                                           */ /**
 * \addtogroup Storage
 * @{
 * \defgroup SCSI SCSI Interfaces
 * @{
 ***********************************************************************
 */

#ifndef _VMKAPI_SCSI_H_
#define _VMKAPI_SCSI_H_

/** \cond never */
#ifndef VMK_HEADER_INCLUDED_FROM_VMKAPI_H
#error This vmkapi file should never be included directly but only via vmkapi.h
#endif
/** \endcond never */

#include "scsi/vmkapi_scsi_const.h"
#include "scsi/vmkapi_scsi_types.h"

/*
 * Physical Path
 */

/*
 ***********************************************************************
 * vmk_ScsiDeviceClassToString --                                 */ /**
 *
 * \brief Convert a SCSI class identifier into a human-readable text
 *        string.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] sclass    SCSI class to convert.
 *
 * \return The description string.
 *
 ***********************************************************************
 */
const char *vmk_ScsiDeviceClassToString(
   vmk_ScsiDeviceClass sclass);

/*
 ***********************************************************************
 * vmk_ScsiScanPaths --                                           */ /**
 *
 * \brief Scan one or more physical paths.
 *
 * The discovered paths are automatically registered with the
 * storage stack.
 *
 * The sparse luns, max lun id and lun mask settings affect which
 * paths are actually scanned.
 *
 * \note If this routine returns an error, some paths may have been
 *       sucessfully discovered.
 *
 * \note This function may block.
 *
 * \param[in] adapterName  Name of the adapter to scan, or
 *                         VMK_SCSI_PATH_ANY_ADAPTER to scan all
 *                         adapters.
 * \param[in] channel      Channel to scan, or VMK_SCSI_PATH_ANY_CHANNEL
 *                         to scan all channels.
 * \param[in] target       Target id to scan, or VMK_SCSI_PATH_ANY_TARGET
 *                         to scan all targets.
 * \param[in] lun          LUN id to scan, or VMK_SCSI_PATH_ANY_LUN to
 *                         scan all LUNs.
 *
 *
 * \retval VMK_INVALID_NAME      The requested adapter was not found.
 * \retval VMK_BUSY              The requested adapter is currently being 
 *                               scanned by some other context.
 * \retval VMK_MAX_PATHS_CLAIMED The limit for maximum no. of paths that 
 *                               can be claimed is reached.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiScanPaths(
   const vmk_Name *adapterName,
   vmk_uint32 channel,
   vmk_uint32 target,
   vmk_uint32 lun);

/*
 ***********************************************************************
 * vmk_ScsiScanAndClaimPaths --                                   */ /**
 *
 * \brief Scan one or more physical paths and run the plugin claim rules.
 *
 * The discovered paths are automatically registered with the
 * storage stack. Path claim is invoked after a successful scan.
 * This may result in new SCSI devices being registered with VMkernel.
 *
 * The sparse luns, max lun id and lun mask settings affect which
 * paths are actually scanned.
 *
 * \note This function may block.
 *
 * \param[in] adapterName  Name of the adapter to scan, or
 *                         \em VMK_SCSI_PATH_ANY_ADAPTER to scan all
 *                         adapters.
 * \param[in] channel      Channel to scan, or 
 *                         \em VMK_SCSI_PATH_ANY_CHANNEL to scan all 
 *                         channels.
 * \param[in] target       Target id to scan, or 
 *                         \em VMK_SCSI_PATH_ANY_TARGET to scan all 
 *                         targets.
 * \param[in] lun          LUN id to scan, or \em VMK_SCSI_PATH_ANY_LUN 
 *                         to scan all LUNs.
 *
 * \return It can return all the errors that vmk_ScsiScanPaths() can return.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiScanAndClaimPaths(
   const vmk_Name *adapterName,
   vmk_uint32 channel,
   vmk_uint32 target,
   vmk_uint32 lun);

/*
 ***********************************************************************
 * vmk_ScsiScanDeleteAdapterPath --                               */ /**
 *
 * \brief Scan a physical path and remove it if dead
 *
 * The path will be unclaimed and removed only if it is dead.
 * This may result in a SCSI device being unregistered with VMkernel
 * in the case where this is the last path backing that device.
 * If there are users of the device and it is the last path to the
 * device, then the unclaim and deletion of the path will fail.
 *
 * The sparse luns, max lun id and lun mask settings affect whether
 * the path is actually scanned or not.
 *
 * \note This function may block.
 *
 * \note Do NOT use a loop around this call to rescan every possible
 *       path on an adapter. This API is only intended for removal of
 *       specific paths based on a plugins better knowledge of such a
 *       path being removed. In general VMware wants the end user to
 *       know about dead paths so misconfigurations can be fixed and
 *       a plugin should thus never remove a path if it does not have
 *       special knowledge from the backing device about the path
 *       being dead and that this knowledge is irrelevant to the user
 *       (e.g. because the user removed the device through device
 *       specific means etc.).
 *
 * \param[in] adapterName  Name of the adapter to scan
 * \param[in] channel      Channel to scan
 * \param[in] target       Target id to scan
 * \param[in] lun          LUN id to scan
 *
 * \return It can return all the errors that vmk_ScsiScanPaths() can
 *         return as well as VMK_BAD_PARAM if the channel/target/lun
 *         is an ANY parameter (you can only scan one specific path).
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiScanDeleteAdapterPath(
   const vmk_Name *adapterName,
   vmk_uint32 channel,
   vmk_uint32 target,
   vmk_uint32 lun);

/*
 ***********************************************************************
 * vmk_ScsiNotifyPathStateChange --                               */ /**
 *
 * \brief Notify the VMkernel of a possible path state change (sync).
 *
 * Path is identified by \em vmkAdapter, \em channel, \em target
 * and \em lun.
 * This interface does the path probe in the calling context.
 * The function returns the result from an attempt to probe the 
 * state of the path(s) without retrying on error conditions.
 *
 * \note This function may block.
 * \note No spin lock may be held when calling this function.
 *
 * \param[in] vmkAdapter   Name of the adapter for state change.
 * \param[in] channel      Channel for state change.
 * \param[in] target       Target for state change.
 * \param[in] lun          LUN for state change. If -1, scan all
 *                         LUNs on the given adapter, channel,
 *                         and target.
 *
 * \retval VMK_NOT_FOUND   Requested path was not found. This will
 *                         be returned only when a specific \em lun 
 *                         is mentioned (not for \em lun==-1).
 * \retval VMK_BUSY        Requested path is already being probed.
 *
 * \return Any other error indicates some plugin specific error while
 *          probing for the path.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiNotifyPathStateChange(
   vmk_ScsiAdapter *vmkAdapter,
   vmk_int32 channel,
   vmk_int32 target,
   vmk_int32 lun);

/*
 ***********************************************************************
 * vmk_ScsiNotifyPathStateChangeAsync --                          */ /**
 *
 * \brief Notify the VMkernel of a possible path state change (async).
 *
 * Path is identified by \em vmkAdapter, \em channel, \em target, 
 * \em lun. Unlike vmk_ScsiNotifyPathStateChange(), which does the
 * path probe in the calling context, this function only schedules 
 * the path probe to happen in separate context. Hence there is no 
 * restriction on the calling context (interrupt, or bottom-half, 
 * or kernel context) and the locks held on entry. 
 *
 * The function returns the result of an attempt to schedule a
 * probe of the state of the path(s). The result of the probe 
 * itself is not returned.  In the face of error conditions, 
 * the probe will be retried a large number of times.
 *
 * \note This function will not block.
 *
 * \param[in] vmkAdapter   Name of the adapter for state change.
 * \param[in] channel      Channel for state change.
 * \param[in] target       Target for state change.
 * \param[in] lun          LUN for state change. If -1, scan all
 *                         LUNs on the given adapter, channel,
 *                         and target.
 * 
 * \retval VMK_NO_MEMORY      Out of memory.
 * \retval VMK_BAD_PARAM      Invalid adapter passed.
 * \retval VMK_NO_RESOURCES   Failed to schedule the asynchronous path 
 *                            probe due to lack of resources.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiNotifyPathStateChangeAsync(
   vmk_ScsiAdapter *vmkAdapter,
   vmk_int32 channel,
   vmk_int32 target,
   vmk_int32 lun);

/*
 ***********************************************************************
 * vmk_ScsiInitTaskMgmt --                                        */ /**
 *
 * \brief Create a task management request filter.
 *
 * A task management request filter consists of two things:
 *
 * - A TASK MANAGEMENT action (abort, device reset, bus reset, lun 
 *   reset etc). This is the \em type parameter.
 *
 * - A rule to define the commands on which the above action should
 *   be performed. This is defined by the \em cmdId parameter.
 *
 * A task management request filter thus created can then be used by any 
 * subsequent functions that expect a \em vmk_ScsiTaskMgmt argument.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] vmkTask   The task management structure to initialize.
 * \param[in] type      Type of task management request.
 * \param[in] cmdId     Identifier of the command(s) to abort.
 *                      This field is only valid for abort and virtual
 *                      reset. It is ignored otherwise. 
 *                      \em VMK_SCSI_TASKMGMT_ANY_INITIATOR is only
 *                      valid for virtual reset.
 *                      cmdID.initiator may one of the following:
 *                      - cmdID.initiator from the vmk_ScsiCommand
 *                        structure.
 *                      - \em VMK_SCSI_TASKMGMT_ANY_INITIATOR.
 *                      - NULL
 *                      If initiator is from the vmk_ScsiCommand 
 *                      structure and type is \em VMK_SCSI_TASKMGMT_ABORT,
 *                      then cmdID.serialNumber can also be obtained
 *                      from vmk_ScsiCommand (cmdID.serialNumber)
 *                      else 0.
 *                      cmdID.serialNumber is only valid for abort.
 *                      It is ignored otherwise.
 * \param[in] worldId   World on behalf of whom the command was issued.
 *                      This argument is only valid for abort and
 *                      virtual reset.  It is ignored otherwise.
 *
 * \par Examples:
 * - Abort a specific I/O:
 *    \code
 *    vmk_ScsiInitTaskMgmt(task, VMK_SCSI_TASKMGMT_ABORT,
 *                         vmkCmd->cmdId, vmkCmd->worldId); 
 *    \endcode
 * - Abort all i/os issued by a single world:
 *    \code
 *    cmdId.initiator = VMK_SCSI_TASKMGMT_ANY_INITIATOR;
 *    cmdId.serialNumber = 0;
 *    vmk_ScsiInitTaskMgmt(task, VMK_SCSI_TASKMGMT_VIRT_RESET,
 *                         cmdId, worldId);
 *    \endcode
 * - Abort all i/os issued by a single world from a specific initiator:
 *    \code
 *    cmdId.initiator = originalInitiator:
 *    cmdId.serialNumber = 0;
 *    vmk_ScsiInitTaskMgmt(task, VMK_SCSI_TASKMGMT_VIRT_RESET,
 *                         cmdId, worldId);
 *    \endcode
 * - Abort all i/os issued by all worlds:
 *    \code
 *    cmdId.initiator = NULL;
 *    cmdId.serialNumber = 0;
 *    vmk_ScsiInitTaskMgmt(task, VMK_SCSI_TASKMGMT_LUN_RESET,
 *                         cmdId, worldId);
 *    \endcode
 *    The worldID should be the World ID of the reset issuer.
 *
 ***********************************************************************
 */
void vmk_ScsiInitTaskMgmt(
   vmk_ScsiTaskMgmt *vmkTask,
   vmk_ScsiTaskMgmtType type,
   vmk_ScsiCommandId cmdId,
   vmk_uint32 worldId);

/*
 ***********************************************************************
 * vmk_ScsiQueryTaskMgmt --                                       */ /**
 *
 * \brief   Matches a SCSI command against a task management request
 *          filter, and returns the action to be taken for that command.
 *
 * The task management request filter is obtained by a call to
 * vmk_ScsiInitTaskMgmt()
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] vmkTaskMgmt     Task management request.
 * \param[in] cmd             Command to check.
 *
 * \retval VMK_SCSI_TASKMGMT_ACTION_IGNORE   No task management action to
 *                                           be taken for this command.
 * \retval VMK_SCSI_TASKMGMT_ACTION_ABORT    Complete this command with
 *                                           status 
 *                                           \em vmkTaskMgmt->\em status
 *
 ***********************************************************************
 */
vmk_ScsiTaskMgmtAction vmk_ScsiQueryTaskMgmt(
   const vmk_ScsiTaskMgmt *vmkTaskMgmt,
   const vmk_ScsiCommand *cmd);

/*
 ***********************************************************************
 * vmk_ScsiGetTaskMgmtTypeName --                                 */ /**
 *
 * \brief Returns a human readable description of the task management
 *        request.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] taskMgmtType    Task management type to convert to a
 *                            string.
 *
 * \return The description string.
 *
 ***********************************************************************
 */
const char *vmk_ScsiGetTaskMgmtTypeName(
   vmk_ScsiTaskMgmtType taskMgmtType);

/*
 ***********************************************************************
 * vmk_ScsiDebugDropCommand --                                    */ /**
 *
 * \brief Tell whether a command should be dropped.
 *
 * This is used for fault injection.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] adapter   Adapter the command would be issued to.
 * \param[in] cmd       Command to check.
 *
 * \retval VMK_TRUE     The command should be dropped.
 * \retval VMK_FALSE    The command should not be dropped.
 *
 * \note vmk_ScsiDebugDropCommand() always returns VMK_FALSE for release
 *       builds.
 *
 ***********************************************************************
 */
vmk_Bool
vmk_ScsiDebugDropCommand(vmk_ScsiAdapter *adapter, vmk_ScsiCommand *cmd);

/*
 ***********************************************************************
 * vmk_ScsiAdapterEvent --                                        */ /**
 *
 * \brief Notifies the VMkernel of a specific event on the
 *        specified adapter.
 *
 * \note This function will not block.
 *
 * \param[in] adapter   Pointer to the adapter to signal the event on.
 * \param[in] eventType Event to signal.
 * 
 * \retval VMK_BUSY        Another adapter event is being processed.
 * \retval VMK_NO_MEMORY   Out of memory.
 *
 * \return  Any other error value indicates some internal error
 *          encountered while notifying VMkernel.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiAdapterEvent(vmk_ScsiAdapter *adapter, vmk_uint32 eventType);

/*
 ***********************************************************************
 * vmk_ScsiAdapterIsPAECapable --                                 */ /**
 *
 * \brief Determines if the adapter supports DMA beyond 32 bits of
 *        machine-address space.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] adapter   Adapter to check.
 *
 * \retval VMK_TRUE     The adapter supports DMA beyond 32 bits.
 * \retval VMK_FALSE    The adapter is limited to DMA in the lower
 *                      32 bits of machine-address space
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiAdapterIsPAECapable(vmk_ScsiAdapter *adapter)
{
   return adapter->paeCapable;
}

/*
 ***********************************************************************
 * vmk_ScsiCmdStatusIsGood --                                     */ /**
 *
 * \brief Determine if a command status indicates a successful
 *        completion.  Note that this function returns false
 *        if the device returns a check condition with a
 *        recovered error sense code.  See vmk_ScsiCmdIsSuccessful()
 *        to test for both conditions.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] status    Status to check.
 *
 * \retval VMK_TRUE     The status indicates successful completion.
 * \retval VMK_FALSE    The status indicates some error. Use
 *                      vmk_ScsiCmdStatusIsCheck() and
 *                      vmk_ScsiExtractSenseData() to get the actual
 *                      error.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdStatusIsGood(vmk_ScsiCmdStatus status)
{
   return (status.device == VMK_SCSI_DEVICE_GOOD &&
           status.host == VMK_SCSI_HOST_OK &&
           status.plugin == VMK_SCSI_PLUGIN_GOOD) ? VMK_TRUE : VMK_FALSE;
}

/*
 ***********************************************************************
 * vmk_ScsiCmdIsRecoveredError --                                 */ /**
 *
 * \brief Determine if a command status indicates a recovered
 *        error.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] vmkCmd    Command to check.
 *
 * \retval VMK_TRUE     The status indicates a command completed with
 *                      a recovered error status.
 * \retval VMK_FALSE    The status indicates a command did not
 *                      complete with a recovered error status.
 *
 ***********************************************************************
 */
vmk_Bool
vmk_ScsiCmdIsRecoveredError(const vmk_ScsiCommand *vmkCmd);

/*
 ***********************************************************************
 * vmk_ScsiCmdIsSuccessful --                                     */ /**
 *
 * \brief Determine if vmk_ScsiCommand completed successfully.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] vmkCmd    vmkCmd to check
 *
 * \retval VMK_TRUE     The status indicates the i/o completed
 *                      successfully
 * \retval VMK_FALSE    The status indicates some error. Use
 *                      vmk_ScsiCmdStatusIsCheck() and
 *                      vmk_ScsiExtractSenseData() to get the actual
 *                      error.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdIsSuccessful(const vmk_ScsiCommand *vmkCmd)
{
   return vmk_ScsiCmdStatusIsGood(vmkCmd->status) ||
      vmk_ScsiCmdIsRecoveredError(vmkCmd);
}

/*
 ***********************************************************************
 * vmk_ScsiCmdStatusIsResvConflict --                             */ /**
 *
 * \brief Determine if a command status indicates a reservation
 *        conflict.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] status    Status to check.
 *
 * \retval VMK_TRUE     The status is reservation conflict.
 * \retval VMK_FALSE    The status is not a reservation conflict.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdStatusIsResvConflict(vmk_ScsiCmdStatus status)
{
   return (status.device == VMK_SCSI_DEVICE_RESERVATION_CONFLICT &&
           status.host == VMK_SCSI_HOST_OK &&
           status.plugin == VMK_SCSI_PLUGIN_GOOD);
}

/*
 ***********************************************************************
 * vmk_ScsiCmdStatusIsCheck --                                    */ /**
 *
 * \brief Determine if a command status indicates a check condition.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] status    Status to check.
 *
 * \retval VMK_TRUE     The status indicates a check condition.
 * \retval VMK_FALSE    The status is not a check condition.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdStatusIsCheck(vmk_ScsiCmdStatus status)
{
   return (status.device == VMK_SCSI_DEVICE_CHECK_CONDITION &&
           status.host == VMK_SCSI_HOST_OK &&
           status.plugin == VMK_SCSI_PLUGIN_GOOD) ? VMK_TRUE : VMK_FALSE;
}

/*
 ***********************************************************************
 * vmk_ScsiCmdSenseIsPOR --                                       */ /**
 *
 * \brief Determine if sense data is a unit attention with Power-On Reset
 *        as the additional sense code.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] senseData sense data to inspect.
 *
 * \retval VMK_TRUE     The status indicates a unit attention with
 *                      POWER ON, RESET, or BUS DEVICE RESET occured. 
 * \retval VMK_FALSE    The status is not a power-on reset unit attention.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdSenseIsPOR(const vmk_ScsiSenseData *senseData)
{
   return ((senseData->key == VMK_SCSI_SENSE_KEY_UNIT_ATTENTION) &&
           (senseData->optLen >= 6) &&
           (senseData->asc == VMK_SCSI_ASC_POWER_ON_OR_RESET) &&
           (senseData->ascq <= 4));
}

/*
 ***********************************************************************
 * vmk_ScsiCmdSenseIsResvReleased --                              */ /**
 *
 * \brief Determine if sense data is a unit attention with reservation
 *        released as the additional sense code.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] senseData sense data to inspect.
 *
 * \retval VMK_TRUE     The senses data is a unit attention with 
 *                      reservation released.
 * \retval VMK_FALSE    The senses data is not a unit attention with 
 *                      reservation released.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdSenseIsResvReleased(const vmk_ScsiSenseData *senseData)
{
   return ((senseData->key == VMK_SCSI_SENSE_KEY_UNIT_ATTENTION) &&
           (senseData->asc == VMK_SCSI_ASC_PARAMS_CHANGED) &&
           (senseData->ascq == 
            VMK_SCSI_ASC_PARAMS_CHANGED_ASCQ_RESERVATIONS_RELEASED));
}

/*
 ***********************************************************************
 * vmk_ScsiCmdSenseIsMediumNotPresent --                          */ /**
 *
 * \brief Determine if sense data is a sense key not ready with medium
 *        not present as the additional sense code.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] senseData sense data to inspect.
 *
 * \retval VMK_TRUE     The senses data is sense key not ready with
 *                      medium not present.
 * \retval VMK_FALSE    The senses data is not a sense key not ready 
 *                      with medium not present.
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiCmdSenseIsMediumNotPresent(const vmk_ScsiSenseData *senseData)
{
   return ((senseData->key == VMK_SCSI_SENSE_KEY_NOT_READY) &&
           (senseData->asc == VMK_SCSI_ASC_MEDIUM_NOT_PRESENT));
}

/*
 ***********************************************************************
 * vmk_ScsiGetLbaLbc --                                           */ /**
 *
 * \brief Parse a SCSI CDB and pull out lba and lbc.
 *
 * Determine the lba and lbc for a given cdb.  This is most useful
 * for READ and WRITE cdb's.  This function will deal with converting
 * endianness & differences between the sizes of cdbs.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in]  cdb         SCSI cdb to parse.
 * \param[in]  cdbLen      Length of cdb in bytes.
 * \param[in]  devClass    SCSI Device Class.
 * \param[out] lba         Logical Block Address.
 * \param[out] lbc         Logical Block Count.
 *
 * \retval VMK_NOT_SUPPORTED     \em devClass is none of 
 *                               \em SCSI_CLASS_DISK, \em SCSI_CLASS_CDROM,
 *                               \em SCSI_CLASS_TAPE, or,
 *                               \em devClass is
 *                               \em SCSI_CLASS_CDROM and \em cdb[0] does 
 *                               not contain 10/12 byte READ/WRITE SCSI
 *                               command opcode.
 * \retval VMK_BAD_PARAM         \em cdb[0] does not contain 6/10/12/16
 *                               byte READ/WRITE SCSI command opcode.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiGetLbaLbc(unsigned char *cdb, vmk_ByteCount cdbLen,
                  vmk_ScsiDeviceClass devClass, vmk_uint64 *lba,
                  vmk_uint32 *lbc);

/*
 ***********************************************************************
 * vmk_ScsiSetLbaLbc --                                           */ /**
 *
 * \brief Set lba and lbc fields in a given SCSI CDB.
 *
 * Set the lba and lbc for a given cdb. This is most useful
 * for READ and WRITE cdb's. This function will deal with converting
 * endianness & differences between the sizes of cdb's. Callers
 * must set \em cdb[0] before making this call.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in,out] cdb         SCSI cdb to set.
 * \param[in]     cdbLen      Length of cdb in bytes.
 * \param[in]     devClass    SCSI Device Class.
 * \param[out]    lba         Logical Block Address.
 * \param[out]    lbc         Logical Block Count.
 *
 * \retval VMK_NOT_SUPPORTED     \em devClass is none of 
 *                               \em SCSI_CLASS_DISK, 
 *                               \em SCSI_CLASS_CDROM, or,
 *                               \em devClass is \em SCSI_CLASS_CDROM and 
 *                               \em cdb[0] does not contain 10/12 byte
 *                               READ/WRITE SCSI command opcode.
 * \retval VMK_BAD_PARAM         \em cdb[0] does not contain 6/10/12/16
 *                               byte SCSI READ/WRITE command opcode.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiSetLbaLbc(unsigned char *cdb, vmk_ByteCount cdbLen,
                  vmk_ScsiDeviceClass devClass, vmk_uint64 *lba,
                  vmk_uint32 *lbc);

/*
 ***********************************************************************
 * vmk_ScsiAllocateAdapter --                                     */ /**
 *
 * \brief Allocate an adapter.
 *
 * \note This function may block.
 *
 * After successful return, the adapter is in \em allocated state.
 * Use vmk_ScsiFreeAdapter() to free this adapter structure.
 *
 * \return New adapter.
 * \retval NULL   Out of memory.
 *
 ***********************************************************************
 */
vmk_ScsiAdapter *vmk_ScsiAllocateAdapter(void);

/*
 ***********************************************************************
 * vmk_ScsiRegisterAdapter --                                     */ /**
 *
 * \deprecated This call should no longer be called directly by a
 *             Native Driver as it is only used internally by PSA.
 *             It is likely to go away in a future release.
 *
 * \brief Register an adapter with the VMkernel.
 *
 * After successful return, the adapter is in \em enabled state and 
 * drivers can issue a scan on that adapter.
 *
 * If \em ScanOnDriverLoad config option is set and 
 * \em vmk_ScsiAdapter->\em flag
 * has \em VMK_SCSI_ADAPTER_FLAG_REGISTER_WITHOUT_SCAN bit clear, then
 * VMkernel will scan all the LUs connected to this adapter. Path 
 * claiming will be run which might result in new SCSI devices being 
 * registered with VMkernel.
 *
 * If you do not want this autoscan and claim process to happen, you can
 * set the \em VMK_SCSI_ADAPTER_FLAG_REGISTER_WITHOUT_SCAN in
 * \em vmk_ScsiAdapter->\em flag, before calling vmk_ScsiRegisterAdapter(). 
 * This can be useful for some drivers f.e. iSCSI, where a usual SCSI 
 * scan does not make sense.
 *
 * \note The scan and claim is done in a deferred context and hence might
 *       not have completed when vmk_ScsiRegisterAdapter() returns.
 *
 * \note This function may block.
 *
 * \param[in] adapter   Adapter to register. This should have been 
 *                      allocated with a prior call to 
 *                      vmk_ScsiAllocateAdapter().
 *
 * \retval VMK_OK          The adapter was successfully registered. Scan 
 *                         and path claim might be still running.
 * \retval VMK_EXISTS      An adapter with same name already registered.
 * \retval VMK_BAD_PARAM   One or more adapter fields are not initialized
 *                         correctly.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiRegisterAdapter(
   vmk_ScsiAdapter *adapter);

/*
 ***********************************************************************
 * vmk_ScsiUnregisterAdapter --                                   */ /**
 *
 * \deprecated This call should no longer be called directly by a
 *             Native Driver as it is only used internally by PSA.
 *             It is likely to go away in a future release.
 *
 * \brief Unregister an adapter previously registered by
 *        vmk_ScsiRegisterAdapter().
 *
 * \note This function may block indefinitely, waiting for all the 
 *       references on the adapter to be released.
 *
 * \param[in] adapter   Adapter to unregister.
 *
 * \retval VMK_OK       Successfully unregistered. The adapter will go
 *                      to \em allocated state and can be used for a
 *                      subsequent vmk_ScsiRegisterAdapter() call.
 * \retval VMK_BUSY     One or more paths originating from this adapter
 *                      are claimed by some plugin.
 * \retval VMK_FAILURE  Adapter is neither in \em enabled, \em disabled
 *                      or \em allocated state. An appropriate message is
 *                      logged.
 * 
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiUnregisterAdapter(
   vmk_ScsiAdapter *adapter);

/*
 ***********************************************************************
 * vmk_ScsiFreeAdapter --                                         */ /**
 *
 * \brief Free an adapter previously allocated by
 *        vmk_ScsiAllocateAdapter().
 * 
 * \warning Do not call vmk_ScsiFreeAdapter() for a registered adapter.
 *          The adapter should be in \em allocated state.
 *
 * \note This function may block.
 *
 * \param[in]  adapter  Adapter to free.
 *
 ***********************************************************************
 */
void vmk_ScsiFreeAdapter(
   vmk_ScsiAdapter *adapter);

/*
 ***********************************************************************
 * vmk_ScsiRemovePath --                                          */ /**
 *
 * \brief Destroy the path identified by \em adapter, \em channel, 
 *        \em targetId and \em lunId.
 *
 * If the path to be removed is claimed by an MP plugin, VMkernel will
 * first try to unclaim the path by calling the plugin's \em unclaim 
 * entrypoint.
 *
 * \note This function may block indefinitely, waiting for all the 
 *       references held on the path to be released.
 *
 * \param[in] adapter   Adapter for path to remove.
 * \param[in] channel   Channel for path to remove.
 * \param[in] targetId  Target ID for path to remove.
 * \param[in] lunId     LUN ID for path to remove.
 *
 * \retval VMK_TRUE     The path was removed successfully.
 * \retval VMK_FALSE    The path doesn't exist or the path exists but is
 *                      claimed by a plugin and could not be successfully 
 *                      unclaimed.
 *
 ***********************************************************************
 */
vmk_Bool vmk_ScsiRemovePath(
   vmk_ScsiAdapter *adapter,
   vmk_uint32 channel,
   vmk_uint32 targetId,
   vmk_uint32 lunId);


/*
 ***********************************************************************
 * vmk_ScsiRegisterIRQ --                                         */ /**
 *
 * \brief Registers an adapter's interrupt handler and interrupt vector
 *        with the VMkernel for polling during a core dump.
 *
 *  To unregister, simply call vmk_ScsiRegisterIRQ() with NULL for 
 *  \em intrHandler.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] adapter            Adapter to register on behalf of.
 * \param[in] intrCookie         Interrupt cookie number to register.
 * \param[in] intrHandler        Interrupt handler callback to invoke
 *                               when an interrupt needs to be serviced.
 * \param[in] intrHandlerData    Private data to pass to the handler.
 *
 * \return  vmk_ScsiRegisterIRQ() always succeeds.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiRegisterIRQ(
   void *adapter,
   vmk_IntrCookie intrCookie,
   vmk_IntrHandler intrHandler,
   void *intrHandlerData);

/*    
 ***********************************************************************
 * vmk_ScsiHostStatusToString --                                  */ /**
 *
 * \brief Take a SCSI host status and return a static string describing it.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] hostStatus   Status to convert.
 *
 * \return Host status as a human readable string.
 *                            
 ***********************************************************************
 */   
char *
vmk_ScsiHostStatusToString(vmk_ScsiHostStatus hostStatus);

/*
 ***********************************************************************
 * vmk_ScsiDeviceStatusToString --                                */ /**
 *
 * \brief Take a SCSI device status and return a static string describing
 *        it.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] deviceStatus    Status to convert.
 *
 * \return Device status as a human readable string.
 *
 ***********************************************************************
 */
char *
vmk_ScsiDeviceStatusToString(vmk_ScsiDeviceStatus deviceStatus);

/*
 ***********************************************************************
 * vmk_ScsiSenseKeyToString --                                    */ /**
 *
 * \brief Take a SCSI sense key and return a static string describing it.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] senseKey   SCSI sense key to convert.
 *
 * \return Sense key as a human readable string.
 *
 ***********************************************************************
 */
char *
vmk_ScsiSenseKeyToString(vmk_uint32 senseKey);

/*
 ***********************************************************************
 * vmk_ScsiAdditionalSenseToString --                             */ /**
 *
 * \brief Take a SCSI ASC/ASCQ and return a static string describing it.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] asc    SCSI ASC value to convert.
 * \param[in] ascq   SCSI ASCQ value to convert.
 *
 * \return ASC/ASCQ as a human readable string.
 *
 ***********************************************************************
 */
char *
vmk_ScsiAdditionalSenseToString(vmk_uint32 asc,
                                vmk_uint32 ascq);

/*                                                                                        
 **********************************************************************
 * vmk_ScsiExtractSenseData --                                   */ /**
 *
 * \brief Extract the SCSI Check Condition.
 *
 * Examine the contents of the senseBuffer and return the SCSI check
 * condtion key, the Additional Sense Code, and the Additional Sense
 * Code Qualifier.
 *
 * \note The routine only handles sense buffers with a response
 *       code of 0x70 or 0x71 (Section 4.5.3 in the SCSI-3 spec).
 *       It does not handle response codes of 0x72, 0x73 or any
 *       vendor specific response codes.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in]  senseBuffer    The sense buffer to be examined.
 * \param[out] sense_key      The sense key to be extracted.
 * \param[out] asc            The Additional Sense Code to be extracted.
 * \param[out] ascq           The Additional Sense Code Qualifier to be
 *                            extracted.
 *
 * \retval VMK_TRUE     The sense buffer indicates a SCSI error condition,
 *                      and sense_key, asc and ascq have been correctly
 *                      set.
 * \retval VMK_FALSE    There was no error indicated on the current or
 *                      previous command.
 *
 ***********************************************************************
 */
vmk_Bool
vmk_ScsiExtractSenseData(vmk_ScsiSenseData *senseBuffer,
                         vmk_uint8 *sense_key,
                         vmk_uint8 *asc,
                         vmk_uint8 *ascq);

/*
 ***********************************************************************
 * vmk_ScsiCmdGetSenseData --                                     */ /**
 *
 * \brief Obtain sense data associated with the given SCSI command.
 *
 * Command is identified by \em vmkCmd.  Sense data is identified by
 * \em buf.  This buffer will be filled with the contents of SCSI command's
 * sense data buffer.  The caller passes in the size of the sense buffer in
 * \em bufLen.  Depending on the number of sense data bytes required, the caller
 * can allocate a buffer of the appropriate size and pass the size of this
 * buffer in \em bufLen.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The max size of SCSI command's sense buffer is obtained by calling
 * vmk_ScsiGetSupportedCmdSenseDataSize.  If the call is made with a buffer
 * larger than the max supported size, only the max supported number of bytes
 * will be set.  The contents of the input buffer beyond that will be set to 0.
 *
 * \note For a small sense buffer, see vmk_ScsiSenseDataSimple.
 *
 * \param[in] vmkCmd       Address of the SCSI command
 * \param[out] buf         Address of the buffer that will
 *                         be filled with sense data
 * \param[in] bufLen       Length of the sense buffer
 *                         above.
 *
 * \retval VMK_OK          Sense data was successfully
 *                         obtained.
 * \retval VMK_BAD_PARAM   A bad argument was passed.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiCmdGetSenseData(vmk_ScsiCommand *vmkCmd,
                        vmk_ScsiSenseData *buf,
                        vmk_ByteCount bufLen);

/*
 ***********************************************************************
 * vmk_ScsiCmdSetSenseData --                                     */ /**
 *
 * \brief Set the sense data of a SCSI command.
 *
 * Command is identified by \em vmkCmd.  Sense data in \em buf is copied
 * to the storage area for sense data in \em vmkCmd.  The number of bytes
 * that will be copied is identified by \em bufLen.  If \em bufLen
 * is less than SCSI command's max sense buffer size(obtained by calling
 * vmk_ScsiGetSupportedCmdSenseDataSize), the remaining bytes in SCSI
 * command's sense buffer are set to 0.  If \em bufLen is larger than
 * SCSI command's max sense buffer size, only max sense buffer size of
 * sense data will be written, ie, sense data will be truncated to the max
 * supported size.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] buf          Address of the buffer that
 *                         holds sense data bytes to be
 *                         copied.
 * \param[in] vmkCmd       Address of the SCSI command
 *                         that contains the destination
 *                         sense buffer.
 * \param[in] bufLen       The number of bytes to be
 *                         copied.
 *
 * \retval VMK_OK          Sense data was successfully
 *                         set.
 * \retval VMK_BAD_PARAM   A bad argument was passed.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiCmdSetSenseData(vmk_ScsiSenseData *buf,
                        vmk_ScsiCommand *vmkCmd,
                        vmk_ByteCount bufLen);

/*
 ***********************************************************************
 * vmk_ScsiCmdClearSenseData --                                   */ /**
 *
 * \brief Clear the sense data attached to a SCSI command
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * Command is identified by \em vmkCmd.
 *
 * \param[in] vmkCmd       Address of the SCSI command
 *
 * \retval VMK_OK          Sense data was successfully
 *                         cleared.
 * \retval VMK_BAD_PARAM   A bad argument was passed.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiCmdClearSenseData(vmk_ScsiCommand *vmkCmd);

/*
 ***********************************************************************
 * vmk_ScsiGetSupportedCmdSenseDataSize --                        */ /**
 *
 * \brief Get the size of the SCSI command's sense data buffer.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \retval The SCSI command's supported buffer size
 *
 ***********************************************************************
 */
vmk_ByteCount
vmk_ScsiGetSupportedCmdSenseDataSize(void);

/*
 ***********************************************************************
 * vmk_ScsiVPDPageSize --                                         */ /**
 *
 * \brief Get the size of a VPD page.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] response  SCSI VPD Inquiry response header. This is usually 
 *                      the SCSI Inquiry response obtained by a prior 
 *                      vmk_ScsiGetPathInquiry() call.
 *                                                                   
 * \return Size of VPD Page in bytes.
 *
 * \note The routine assumes that VPD length field is one byte for
 *       all pages except for page 83 which has 2 byte length.
 *
 ***********************************************************************
 */
vmk_ByteCountSmall 
vmk_ScsiVPDPageSize(vmk_ScsiInquiryVPDResponse *response);

/*
 ***********************************************************************
 * vmk_ScsiIllegalRequest --                                      */ /**
 *
 * \brief Generates "illegal request" sense buffer data.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[out] senseBuffer   The sense buffer to be modified
 * \param[in]  asc           The Additional Sense Code
 * \param[in]  ascq          The Additional Sense Code Qualifier
 *
 * \note Status must be set to VMK_SCSI_DEVICE_CHECK_CONDITION
 *       in order for the sense buffer to be examined.
 *
 ***********************************************************************
 */
void
vmk_ScsiIllegalRequest(vmk_ScsiSenseData *senseBuffer,
                       vmk_uint8 asc,
                       vmk_uint8 ascq);

/*
 ***********************************************************************
 * vmk_ScsiIsReadCdb --                                           */ /**
 *
 * \brief   Check whether the given SCSI opcode is one of the READ
 *          commands. 
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \retval VMK_TRUE  Opcode is a READ CDB opcode.
 * \retval VMK_FALSE Opcode is not a READ CDB opcode. 
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiIsReadCdb(vmk_uint8 cdb0)
{
   return cdb0 == VMK_SCSI_CMD_READ6 
      || cdb0 == VMK_SCSI_CMD_READ10
      || cdb0 == VMK_SCSI_CMD_READ12
      || cdb0 == VMK_SCSI_CMD_READ16;
}

/*
 ***********************************************************************
 * vmk_ScsiIsWriteCdb --                                          */ /**
 *
 * \brief   Check whether the given SCSI opcode is one of the WRITE
 *          commands. 
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \retval VMK_TRUE  Opcode is a WRITE CDB opcode.
 * \retval VMK_FALSE Opcode is not a WRITE CDB opcode. 
 *
 ***********************************************************************
 */
static inline vmk_Bool
vmk_ScsiIsWriteCdb(vmk_uint8 cdb0)
{
   return cdb0 == VMK_SCSI_CMD_WRITE6 
      || cdb0 == VMK_SCSI_CMD_WRITE10
      || cdb0 == VMK_SCSI_CMD_WRITE12
      || cdb0 == VMK_SCSI_CMD_WRITE16;
}

/*
 ***********************************************************************
 * vmk_ScsiGetAdapterName --                                      */ /**
 *
 * \brief Return the adapter's name
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This function will not block.
 *
 ***********************************************************************
 */
static inline const char *
vmk_ScsiGetAdapterName(const vmk_ScsiAdapter *vmkAdapter)
{
   return vmk_NameToString((vmk_Name *)&vmkAdapter->name);
}

/*                                                                                        
 **********************************************************************                  
 * vmk_ScsiSetPathXferLimit --                                   */ /**                  
 *                                                                                        
 * \brief Set the maximum single transfer size limit for a path.
 *                                                             
 * \param[in] vmkAdapter   The target adapter.
 * \param[in] channel      The target channel.
 * \param[in] target       The target number.
 * \param[in] lun          The target LUN.
 * \param[in] maxXfer      The new maximum transfer size in bytes.
 *
 * \retval VMK_OK          The max transfer size was set successfully.
 * \retval VMK_NOT_FOUND   The requested path was not found.
 *
 * \warning Do not set maximum transfer size to 0. This will result
 *          in undefined behaviour.
 *
 * \note    You should not set maximum transfer size to a value greater
 *          than the adapter's maximum transfer size. Most adapters 
 *          support a maximum transfer size of 256K, so it is safe to 
 *          set a \em maxXfer value <= 256K.
 *
 * \note This function will not block.
 *
 ***********************************************************************                  
 */
VMK_ReturnStatus
vmk_ScsiSetPathXferLimit(vmk_ScsiAdapter *vmkAdapter,
                         vmk_int32 channel,
                         vmk_int32 target,
                         vmk_int32 lun,
                         vmk_ByteCount maxXfer);

/*                                                                                        
 **********************************************************************
 * vmk_ScsiModifyQueueDepth --                                   */ /**
 *
 * \brief Set the queue depth of the path specified by \em vmkAdapter 
 *       \em channel, \em target, and \em lun.
 *
 * For multiple paths going through the same SCSI adapter, the path
 * queue depth controls the share of that path in the adapter's queue.
 * Setting path queue depth to a value greater than the underlying 
 * adapter's queue depth is inconsequential and should not be done.
 *        
 * \note This function will not block.
 *
 * \param[in] vmkAdapter   The target adapter.
 * \param[in] channel      The target channel.
 * \param[in] target       The target number.
 * \param[in] lun          The target LUN.
 * \param[in] qdepth       The new queue depth value.
 *
 * \retval VMK_OK          The queue depth was set successfully.
 * \retval VMK_NOT_FOUND   The requested path was not found.
 *
 * \warning Do not set qdepth to 0. This will result in undefined 
 *          behaviour.
 *
 ***********************************************************************                  
 */
VMK_ReturnStatus
vmk_ScsiModifyQueueDepth(vmk_ScsiAdapter *vmkAdapter,
                         vmk_int32 channel,
                         vmk_int32 target,
                         vmk_int32 lun,
                         vmk_uint32 qdepth);

/*
 ***********************************************************************
 * vmk_ScsiAllFCPathsDown --                                      */ /**
 *
 * \brief Check if all Fibre Channel transport paths are dead.
 *
 * \note This function will not block.
 *
 * \retval VMK_TRUE  There are one or more Fibre Channel paths but all
 *                   of them are currently down.
 * \retval VMK_FALSE There are no Fibre Channel paths or one or more 
 *                   Fibre Channel paths are up.
 *
 ***********************************************************************
 */
vmk_Bool vmk_ScsiAllFCPathsDown(void);

/*
 ***********************************************************************
 * vmk_ScsiAdapterUniqueName --                                   */ /**
 *
 * \ingroup DeviceName
 * \deprecated This call should no longer be called directly by a
 *             Native Driver as it is only used internally by PSA.
 *             It is likely to go away in a future release.
 *
 * \brief Create a new unique adapter name.
 *
 * This function returns a new unique adapter name.
 *
 * \note This function will not block.
 *
 * \param[out] adapterName  Pointer to an element of type vmk_Name to
 *                          receive the new adapter name.
 ***********************************************************************
 */
void vmk_ScsiAdapterUniqueName(vmk_Name *adapterName);

/*
 ***********************************************************************
 * vmk_ScsiGetIdentifierFromPage83Inquiry --                      */ /**
 *
 * \brief Get a UID of given type from a page 83 inquiry buffer
 *
 * Get the identifier of the given idType from the given page 83
 * inquiry data buffer. The id descriptor is returned if parameter
 * desc is non-NULL.
 *
 * \note The returned descriptor is not NULL terminated (many ID types
 *       can contain NULL as valid data). The length of the returned
 *       descriptor is in the desc->idLen field.
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in]  inquiryBuf     Buffer with VPD page 83
 * \param[in]  inquiryBufLen  Length of the inquiryBuf buffer
 * \param[in]  idType         SCSI identifier type
 * \param[in]  assoc          SCSI association type
 * \param[out] id             Identifier buffer
 * \param[in]  idLength       Size of identifier buffer
 * \param[out] desc
 *
 * \retval VMK_NOT_FOUND   An identifier descriptor of the given idType
 *                         could not be found in the page 83 inquiry 
 *                         response
 * \retval VMK_OK          An identifier descriptor of the given idType
 *                         was found and the identifier was copied into
 *                         the provided id buffer
 * \retval VMK_BAD_PARAM   An identifier descriptor of the given idType
 *                         was found but the identifier was larger than
 *                         the provided id[] array.
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiGetIdentifierFromPage83Inquiry(vmk_uint8 *inquiryBuf,
                                       vmk_ByteCountSmall inquiryBufLen,
                                       vmk_ScsiIdentifierType idType,
                                       vmk_ScsiAssociationType assoc,
                                       vmk_uint8 *id,
                                       vmk_ByteCountSmall  idLength,
                                       vmk_ScsiInquiryVPD83IdDesc *desc);

/*
 ***********************************************************************
 * vmk_ScsiGetSystemLimits --                                     */ /**
 *
 * \brief Retrieve the max number of Scsi devices and paths supported
 *
 * \note This function will not block.
 *
 * \param[out] limits   pointer to a structure containing 
 *                      max number of supported devices and paths
 *
 ***********************************************************************
 */
void
vmk_ScsiGetSystemLimits(vmk_ScsiSystemLimits *limits);

/*
 ***********************************************************************
 * vmk_ScsiCommandGetCompletionHandle --                          */ /**
 *
 * \deprecated Do not use in Native Drivers - please see
 *             vmk_ScsiCommandGetCompletionQueue instead
 *
 * \brief Get the Completion handle for the SCSI command.
 *
 * This function will return the Completion handle which the VMkernel
 * wants the lower layer to preferably use to process this command.
 *
 * \pre Adapter's Completion Objects should have been registered.
 *
 * \see  vmk_ScsiRegisterCompObjects()
 *
 * \note This function will not block.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] vmkAdapter SCSI Adapter for which handle is extracted.
 * \param[in] vmkCmd     vmk_ScsiCommand for which handle is extracted.
 *
 ***********************************************************************
 */
vmk_ScsiCompletionHandle
vmk_ScsiCommandGetCompletionHandle(vmk_ScsiAdapter *vmkAdapter,
                                   vmk_ScsiCommand *vmkCmd);

/*
 ***********************************************************************
 * vmk_ScsiRegisterCompObjects --                                 */ /**
 *
 * \deprecated Do not use in Native Drivers - please see
 *             vmk_ScsiStartCompletionQueues instead
 *
 * \brief Register the completion objects for the adapter with vmkernel
 *
 * This function registers the completion objects created by vmkLinux
 * for the adapter with the VMkernel. The VMkernel will save the handle
 * for the completion objects internally.
 * While processing the SCSI command the adapter can ask the VMkernel 
 * for the completion object in the adapter to be used to process the
 * command.
 *
 * \pre  The adapter should have been registered with vmkernel
 *
 * \see  vmk_ScsiCommandGetCompletionHandle()
 *
 * \note This function may block.
 *
 * \param[in] vmkAdapter     ScsiAdapter having the Completion Object
 * \param[in] numCompObjects number of Completion Objects.
 * \param[in] compObj        array of compObjects created by lower layer
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiRegisterCompObjects(vmk_ScsiAdapter *vmkAdapter,
                                             vmk_uint32 numCompObjects,
                                             vmk_ScsiCompObjectInfo compObj[]);

/*
 ***********************************************************************
 * vmk_ScsiGetNumCompObjects --                                   */ /**
 *
 * \deprecated Do not use in Native Drivers - please see
 *             vmk_ScsiGetMaxNumCompletionQueues instead
 *
 * \brief Queries VMKernel for number of Completion Objects
 *
 * \note This function will not block.
 *
 * \return Maximum Number of Completion Objects that can be created 
 *
 ***********************************************************************
 */
vmk_uint32 vmk_ScsiGetNumCompObjects(void);

/*
 ***********************************************************************
 * vmk_ScsiGetMaxNumCompletionQueues --                           */ /**
 *
 * \brief Provide max number of Completion queues to create for adapter
 *
 * \note This function will not block.
 *
 * \return Maximum Number of Completion queues that can be created for
 *         any adapter.
 *
 ***********************************************************************
 */
vmk_uint32 vmk_ScsiGetMaxNumCompletionQueues(void);

/*
 ***********************************************************************
 * vmk_ScsiStartCompletionQueues --                               */ /**
 *
 * \brief Create Completion queues and completion worlds for an adapter
 *
 * PSA will go ahead and create the number of completion queues passed
 * as well as the associated completion worlds to drive up completions.
 * This means that the driver can call vmkCmd->done from any context
 * since the command will simply be queued and driven to completion
 * by a separate completion world.
 *
 * \note This function may block.
 *
 * \note There will always be one completion world for native adapters
 *       so the driver never has to create it's own completion worlds.
 *
 * \note The number of completion worlds can be raised dynamically, but
 *       cannot currently be decreased. The completion worlds will be
 *       destroyed when the adapter is unregistered, so no special
 *       cleanup has to be done for multiple completion queues.
 *
 * \note If anything fails when creating or extending the number of
 *       completion worlds we will leave the number of completion
 *       worlds to what it was previously.
 *
 * \param[in] vmkAdapter   ScsiAdapter to create/increase queues for
 * \param[in] numQueues    Number of Completion queues to create
 *
 * \retval  VMK_OK         If queues were created, error otherwise
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiStartCompletionQueues(vmk_ScsiAdapter *vmkAdapter,
                              vmk_uint32 numQueues);

/*
 ***********************************************************************
 * vmk_ScsiCommandGetCompletionQueue --                           */ /**
 *
 * \brief Get the Completion queue that should be used for a command
 *
 * This function is for instance used by the driver before issuing the
 * command so it can be issued to the right adapter queue (what the
 * VMkernel would like as it optimizes this dynamically based on what
 * PCPUs a given VM/issuer is running on).
 *
 * \note This function will not block.
 *
 * \param[in] vmkAdapter   ScsiAdapter having the Completion queue
 * \param[in] vmkCmd       The SCSI command to get queue for
 *
 * \returns The queue number to issue this command on
 *
 ***********************************************************************
 */
vmk_uint32
vmk_ScsiCommandGetCompletionQueue(vmk_ScsiAdapter *vmkAdapter,
                                  vmk_ScsiCommand *vmkCmd);

/*
 ***********************************************************************
 * vmk_ScsiCheckPluginRegistered --                               */ /**
 *
 * \brief Check if Plugin is registered.
 *
 * \note This function will not block.
 *
 * \retval  VMK_OK         If plugin is registered.
 * \retval  VMK_NOT_FOUND  If plugin is not registered.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiCheckPluginRegistered(const char *plugin);

/*
 ***********************************************************************
 * vmk_ScsiGetRegisteredModuleName --                             */ /**
 *
 * \brief Get the the Module name for the Plugin specified.
 *
 * \note This function will not block.
 *
 * \param[in]   plugin   Plugin name.
 * \param[out]  name     Buffer for module name.
 *
 * \retval  VMK_OK             If plugin module name is copied to "name".
 * \retval  VMK_NOT_FOUND      If plugin is not registered.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiGetRegisteredModuleName(const char *plugin, vmk_Name *name);

/*
 ***********************************************************************
 * vmk_ScsiResolveRegisteredPluginDependencies --                 */ /**
 *
 * \brief Resolve all dependencies for module "name".
 *        Dependent Modules may get loaded.
 *
 * \note This function may block.
 *
 * \returns VMK_OK         If all dependencies are resolved.
 *          VMK_NOT_FOUND  If module "name" is not registered.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiResolveRegisteredPluginDependencies(const char *name);

/*
 ***********************************************************************
 * vmk_ScsiCmdGetVMUuid --                                        */ /**
 *
 * \brief Get the UUID of the VM associated with the specified command.
 *
 * \note This function will not block.
 *
 * \param[in]  vmkCmd  Scsi command whose associated VM UUID is queried.
 * \param[out] uuid    Pointer to a buffer to fill in with the UUID.
 * \param[in]  len     Length of the supplied buffer.
 *
 * \returns VMK_OK              uuid updated with the VM UUID.
 *          VMK_NOT_FOUND       VM UUID could not be found.
 *          VMK_NOT_INITIALIZED No VM UUID has been set for this VM.
 *          VMK_BUF_TOO_SMALL   Supplied buffer is too small.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiCmdGetVMUuid(vmk_ScsiCommand *vmkCmd, char *uuid, vmk_ByteCount len);

/*
 ***********************************************************************
 * vmk_ScsiSetPathLostByDevice --                               */ /**
 *
 * \brief Mark a path as permanently lost by device, triggering PDL
 *
 * Path is identified by \em adaperName, \em channel, \em target
 * and \em lun.
 * This interface updates the path flags in the calling context.
 *
 * \note This function may block.
 * \note No spin lock may be held when calling this function.
 *
 * \param[in] adapterName  Name of the adapter for state change.
 * \param[in] channel      Channel for state change.
 * \param[in] target       Target for state change.
 * \param[in] lun          LUN for state change. If -1, scan all
 *                         LUNs on the given adapter, channel,
 *                         and target.
 *
 * \retval VMK_OK          Path state successfully updated.
 *
 * \retval VMK_NOT_FOUND   Requested adapter or path was not found.
 *                         This will be returned for a path only when
 *                         a specific \em lun is supplied (not for
 *                         \em lun==-1).
 *
 * \retval VMK_BAD_PARAM   Adapter name is NULL.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiSetPathLostByDevice(const vmk_Name *adapterName,
                            vmk_int32 channel,
                            vmk_int32 target,
                            vmk_int32 lun);

/*
 ***********************************************************************
 * vmk_ScsiSchedCommandCompletion --                              */ /**
 *
 * \brief Schedules a non-blocking context to complete the command.
 *
 * This function schedules a non-blocking context to complete a
 * command. The intent is to use this from the issuing path where
 * a command cannot be completed directly since that could lead to
 * stack exhaustion due to recursive calls to the issuing path from
 * the completion path.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] command   The cmd to complete.
 *
 ***********************************************************************
 */
void vmk_ScsiSchedCommandCompletion(
   vmk_ScsiCommand *command);

#endif  /* _VMKAPI_SCSI_H_ */
/** @} */
/** @} */