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

/*
 * @VMKAPIMOD_LICENSE@
 */

/*
 ***********************************************************************
 * MPP                                                            */ /**
 * \addtogroup Storage
 * @{
 * \defgroup MPP Multi-Pathing Plugin Interfaces
 * @{
 ***********************************************************************
 */

#ifndef _VMKAPI_MPP_H_
#define _VMKAPI_MPP_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.h"
#include "scsi/vmkapi_scsi_mgmt_types.h"
#include "scsi/vmkapi_scsi_ext.h"
#include "scsi/vmkapi_scsi_const.h"
#include "scsi/vmkapi_scsi_types.h"
#include "mpp/vmkapi_mpp_types.h"

/** \cond never */
#define VMK_SCSI_REVISION_MAJOR        1
#define VMK_SCSI_REVISION_MINOR        0
#define VMK_SCSI_REVISION_UPDATE       0
#define VMK_SCSI_REVISION_PATCH_LEVEL  0

#define VMK_SCSI_REVISION VMK_REVISION_NUMBER(VMK_SCSI)
/** \endcond never */

/** \brief Max length of vendor name string including terminating nul. */
#define VMK_SCSI_VENDOR_NAME_LENGTH (VMK_SCSI_INQUIRY_VENDOR_LENGTH + 1)

//** \brief Max length of model name string including terminating nul. */
#define VMK_SCSI_MODEL_NAME_LENGTH (VMK_SCSI_INQUIRY_MODEL_LENGTH + 1)

/** \brief Default name used for unregistered devices */
#define VMK_SCSI_UNREGISTERED_DEV_NAME  "Unregistered Device"

/**
 * \brief Choices for probe rate for vmk_ScsiSwitchDeviceProbeRate.
 */
typedef enum {
   /** \brief For normal operation (once/5 Sec.) */
   VMK_SCSI_PROBE_RATE_DEFAULT = 1,
   /** \brief Selected when the device is in APD (once/Sec.) */
   VMK_SCSI_PROBE_RATE_FAST = 2
} vmk_ScsiDeviceProbeRate;

/**
 * \brief Flags defined for vmk_ScsiSwitchDeviceProbeRate.
 */
typedef enum {
   /** \brief Revert to VMK_SCSI_PROBE_RATE_DEFAULT after next probe. */
   VMK_SCSI_ONE_PROBE_ONLY = 0x00000001
} vmk_ScsiSetDeviceProbeRateOption;


/*
 ***********************************************************************
 * vmk_ScsiGetCachedPathStandardUID --                            */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the cached NAA, EUI, IQN, or TIO UID for physical path.
 *
 * The function returns the cached UID saved with the path as recorded
 * during the last rescan (since rescans will cause UIDs of all paths
 * to be verified). Success may be returned even if the path to the
 * device is not working. Plugins can use this API function in the
 * plugin's \em pathClaim entrypoint.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to obtain cached UID for.
 * \param[out] uid   Obtained UID on success.
 *
 * \retval VMK_BAD_PARAM  The passed in path or uid was null.
 * \retval VMK_FAILURE    The path does not have a standard UID.
 * \retval VMK_OK         Otherwise.
 *
 ***********************************************************************
 */ 
VMK_ReturnStatus vmk_ScsiGetCachedPathStandardUID(
   vmk_ScsiPath *path,
   vmk_ScsiUid *uid);

/*
 ***********************************************************************
 * vmk_ScsiReadPathStandardUID --                                 */ /**
 *
 * \ingroup MPP
 *
 * \brief Read the NAA, EUI, IQN, or TIO UID for physical path from
 *        the device.
 *
 * This function reads the UID from the SCSI device logical unit
 * that the path refers to and will return failure if the path is
 * not working. This API function is useful to check whether the UID
 * for a given path has changed; for example, in the plugin's
 * \em pathProbe entrypoint.
 *
 * \note This will regenerate the UID saved with the path.
 * \note This is a blocking call.
 *
 * \param[in] path   Path to acquire standard UID for.
 * \param[out] uid   Obtained UID on success.
 *
 * \retval VMK_OK               The UID was successfully obtained.
 * \retval VMK_BAD_PARAM        The passed in path or uid was null.
 * \retval VMK_MEDIUM_NOT_FOUND Bad SCSI version or device class
 * \retval VMK_UID_CHANGED      Path is connected but the existing
 *                              devclass has changed. This may indicate
 *                              a change in the existing device.
 * \retval VMK_PERM_DEV_LOSS    LUN is no longer connected
 * \retval Other                Failed to obtain the UID.
 *
 ***********************************************************************
 */ 
VMK_ReturnStatus vmk_ScsiReadPathStandardUID(
   vmk_ScsiPath *path,
   vmk_ScsiUid *uid);

/*
 ***********************************************************************
 * vmk_ScsiVerifyPathUID --                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Re-read path standard or legacy UID and verify that 
 *        it did not change.
 *
 * This function will reread the standard path UID from the device and
 * compare it against the cached UID obtained during the last rescan.
 *
 * \note This is a blocking call.
 *
 * \param[in] path   Path to check UID for.
 * \param[out] uid   UID read from disk.
 *
 * \retval VMK_OK             UID was generated and did not change.
 * \retval VMK_UID_CHANGED    New UID was detected.
 * \retval VMK_PERM_DEV_LOSS  LUN is no longer connected.
 * \retval VMK_NO_CONNECT     Path connectivity has failed.
 * \retval VMK_BAD_PARAM      vmkPath or uid is NULL.
 *
 ***********************************************************************
 */ 
VMK_ReturnStatus vmk_ScsiVerifyPathUID(
   vmk_ScsiPath *path,
   vmk_ScsiUid *uid);

/*
 ***********************************************************************
 * vmk_ScsiComputeUidFromEvpd83 --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Get NAA, EUI, IQN, or TIO UID for physical device using
 *        caller supplied inquiry evpd page 0x83 data.
 *
 * This function will compute the UID from the passed in evpd83Buf.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The size of the \em evpd83Buf has to to be at least as big as
 *       indicated by its 16-bit page length field.  
 *
 * \param[in] vmkPath    Path to acquire inquiry evpd page 0x83.
 * \param[in] evpd83Buf  Buffer to store evpd page 0x83 data.
 * \param[out] uid       UID.
 *
 * \retval VMK_OK             UID was generated and did not change.
 * \retval VMK_FAILURE        No usable UID type (NAA/EUI/IQN/T10)
 *                            was found in the passed buffer.
 ***********************************************************************
 */ 
VMK_ReturnStatus
vmk_ScsiComputeUidFromEvpd83(
   vmk_ScsiPath *vmkPath,
   vmk_uint8 *evpd83Buf,
   vmk_ScsiUid *uid);

/*
 ***********************************************************************
 * vmk_ScsiGetPathTransportUID --                                 */ /**
 *
 * \ingroup MPP
 *
 * \brief Get Path Transport UID for the physical path.
 *
 * This function will obtain a pair of UIDs - one UID for the HBA
 * endpoint and one for the target endpoint of the path. The UID is
 * transport dependent - for FC each endpoint will be of the form
 * WWNN:WWPN and for other transports each endpoint will be whatever
 * unique identifier is used in place for traditional target IDs
 * (e.g. for iSCSI each endpoint will be an IQN).
 *
 * \note This is a non-blocking call.
 *
 * \note This call will fail during device unregistration and removal;
 *       as soon as the device state changes to VMK_SCSI_DEVICE_STATE_QUIESCED
 *       the device will become effectively invisible.
 *
 * \param[in] path   Path to acquire transport uids for.
 * \param[out] uid   Obtained UIDs on success.
 *
 * \retval VMK_OK             UID successfully obtained.
 * \retval VMK_BAD_PARAM      Either path or uid is NULL.
 * \retval VMK_NOT_FOUND      The UIDs could not be obtained.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetPathTransportUID(
   vmk_ScsiPath *path,
   vmk_ScsiPathUid *uid);

/*
 ***********************************************************************
 * vmk_ScsiUIDsAreEqual --                                        */ /**
 *
 * \ingroup MPP
 *
 * \brief compare two uids and return true if they are equal.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] uid1   First UID to compare.
 * \param[in] uid2   Second UID to compare.
 *
 * \retval VMK_TRUE   The UIDs are equal.
 * \retval VMK_FALSE  The UIDs are not equal.
 *
 ***********************************************************************
 */
vmk_Bool vmk_ScsiUIDsAreEqual(
   vmk_ScsiUid *uid1,
   vmk_ScsiUid *uid2);

/*
 ***********************************************************************
 * vmk_ScsiAllocateDevice --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Allocate a logical storage device data structure.
 *
 * This function will allocate space for a vmk_ScsiDevice structure.
 * Obtaining this structure also ensure that the call to register
 * the device will not fail due to max. number of devices already
 * registered. Since the success of this call takes up a slot for a
 * registered SCSI device it is important that such allocations are
 * not left around idle, but are either fully registered or freed
 * again as soon as possible through vmk_ScsiFreeDevice().
 *
 * \see vmk_ScsiFreeDevice().
 *
 * \see vmk_ScsiRegisterDevice().
 *
 * \note This is a blocking call.
 * \note The call also allocates and initializes various associated
 *       VMkernel private data structures.
 * \note This call should only be used by MP plugins. 
 *
 * \post The obtained device MUST be freed through vmk_ScsiFreeDevice().
 *
 * \param[in] plugin Plugin allocating the device.
 *
 * \return Pointer to vmk_ScsiDevice or NULL if allocation fails or
 *         if max. number of SCSI devices are already registered.
 *
 ***********************************************************************
 */
vmk_ScsiDevice *vmk_ScsiAllocateDevice(
   vmk_ScsiPlugin *plugin);

/*
 ***********************************************************************
 * vmk_ScsiRegisterDevice --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Add a logical storage device.
 *
 * This function will attempt to register a logical SCSI device with
 * the VMkernel. There must be at least one UID passed in and precisely
 * one of the UIDs must have \em VMK_SCSI_UID_FLAG_PRIMARY set in its
 * \em uid->idFlags. The \em device->ops field must point to a structure
 * filled with at least the \em startCommand, \em taskMgmt, \em open,
 * \em close, \em probe, \em getInquiry, \em issueDumpCmd,
 * \em isPseudoDevice and \em u.mpDeviceOps.getPathnames function
 * pointers.
 *
 * \see vmk_ScsiDeviceUIDAdd().
 *      vmk_ScsiUnregisterDevice().
 *
 * \note This is a blocking call.
 * \note The plugin will be pinned (unable to unload) until the device
 *       has been unregistered again
 * \note This call should only be used by MP plugins. 
 *
 * \pre The caller MUST have obtained the passed vmk_ScsiDevice
 *      through a call to vmk_ScsiAllocatedevice().
 * \pre The device MUST be ready to accept I/O when the call is made.
 *
 * \param[in] device    Device to register.
 * \param[in] uid       Pointer to an array of numUids uids
 *                      to register for this device.
 * \param[in] numUids   Number of UIDs in the **uid list.
 *
 * \retval VMK_OK                      Device successfully registered.
 * \retval VMK_BAD_PARAM               The value of numUids is less than 1,
 *                                     there is not exactly one uid marked
 *                                     with the VMK_SCSI_UID_FLAG_PRIMARY,
 *                                     the device->ops are not specified
 *                                     correctly, or same as
 *                                     vmk_ScsiDeviceUIDAdd().
 * \retval VMK_NOT_SUPPORTED           The plugin controlling the device
 *                                     is not marked as
 *                                     VMK_SCSI_PLUIGN_TYPE_MULTIPATHING
 *                                     or same as vmk_ScsiDeviceUIDAdd().
 * \retval VMK_NOT_FOUND               No paths are specified for the device.
 * \retval VMK_EXISTS                  Same as vmk_ScsiDeviceUIDAdd().
 * \retval VMK_DUPLIATE_UID            Same as vmk_ScsiDeviceUIDAdd().
 * \retval VMK_TOO_MANY_ELEMENTS       Same as vmk_ScsiDeviceUIDAdd().
 * \retval VMK_FAILURE                 Could not determine the legacy UID
 *                                     for the device.
 * \retval VMK_NO_CONNECT              A path to the device is in the
 *                                     process of  being removed.
 * \retval VMK_TIMEOUT                 I/O command did not complete within
 *                                     the timeout time due to a transient
 *                                     errors.
 * \retval VMK_ABORTED                 I/O command did not complete and
 *                                     was aborted.
 * \retval VMK_BUSY                    I/O command did not complete because
 *                                     the device was busy or there was a
 *                                     race to register/unregister the same
 *                                     device with another thread.
 * \retval VMK_RESERVATION_CONFLICT    I/O command did not complete due
 *                                     to a SCSI reservation on the device.
 * \retval VMK_STORAGE_RETRY_OPERATION I/O command did not complete
 *                                     due to a transient error.
 * \retval VMK_HBA_ERROR               I/O command did not complete due
 *                                     to an HBA or  driver error.
 * \retval VMK_IO_ERROR                I/O command did not complete due
 *                                     to an  unknown error.
 * \retval VMK_NOT_SUPPORTED           I/O command did not complete due
 *                                     to an unspecified error.
 * \retval VMK_MEDIUM_NOT_FOUND        I/O command did not complete on a
 *                                     removeable media device and media
 *                                     is not present.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiRegisterDevice(
   vmk_ScsiDevice *device,
   vmk_ScsiUid **uid,
   vmk_int32 numUids);

/*
 ***********************************************************************
 * vmk_ScsiUnregisterDevice --                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Remove a logical storage device.
 *
 * This function will wait for outstanding Task Management operations
 * on the device to drain before finally unregistering the device.
 * If the device is open by any world the call will fail.
 *
 * \see vmk_ScsiRegisterDevice().
 * \see vmkScsiFreeDevice().
 *
 * \note This is a blocking call.
 * \note The caller should not hold any semaphores.
 * \note This call should only be used by MP plugins. 
 *
 * \pre The device MUST have been successfully registered with a call
 *      to vmk_ScsiRegisterDevice().
 *
 * \param[in] device    Device to unregister.
 *
 * \retval VMK_OK          Device successfully unregistered.
 * \retval VMK_BUSY        Device not unregistered as the device
 *                         was busy.
 * \retval VMK_BAD_PARAM   Device has already been unregistered.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiUnregisterDevice(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiFreeDevice --                                          */ /**
 *
 * \ingroup MPP
 *
 * \brief Free the storage associated with a logical storage device.
 *
 * This function will undo the work of vmk_ScsiAllocateDevice() and
 * will both free the structure and also clean up some associated
 * VMkernel internal structures. Among this is the freeing of the
 * device "slot" that will limit the total number of allocated devices.
 * It is therefore important that this function is used to free the
 * vmk_ScsiDevice structure allocated through vmk_ScsiAllocateDevice().
 *
 * \see vmk_ScsiUnregisterDevice().
 * \see vmk_ScsiAllocateDevice().
 *
 * \note This is a blocking call.
 * \note This call should only be used by MP plugins. 
 *
 * \pre The device MUST have been unregistered (or never registered).
 *
 * \param[in] device    Device to free.
 *
 ***********************************************************************
 */
void vmk_ScsiFreeDevice(
   vmk_ScsiDevice *device); 

/*
 ***********************************************************************
 * vmk_ScsiSetDeviceState--                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Set current device state.
 *
 * This function allows a plugin to set the state of a device. The
 * intent of this call is to allow the user to see the state of a
 * device through the UI or CLI.
 *
 * \note  This is a non-blocking call except when called with 
 *        VMK_SCSI_DEVICE_STATE_QUIESCED. vmk_ScsiSetDeviceState with
 *        VMK_SCSI_DEVICE_STATE_QUIESCED is a blocking call.
 *
 * \note  Spin locks can be held while calling into this function
 *        except when it can block (e.g. VMK_SCSI_DEVICE_STATE_QUIESCED).
 *
 * \note  A device coming back after a permanent device loss event is
 *        considered a user error. There are NO data consistency
 *        guarantees if this occurs. MPPS must set the state to 
 *        VMK_SCSI_DEVICE_STATE_PERM_LOSS indication IFF all paths to
 *        the device have hit a permanent device loss condition. ESX
 *        will fail back all I/Os and abort I/Os in progress. MPP can
 *        expect aborts (virt resets) for I/Os in flight or Qed. MPPs
 *        may choose to return (non-VMK_SCSI_COMMAND_FLAGS_NO_CONNECT_IF_APD)
 *        Qed I/Os but this is optional.  ESX will also generate a VOB
 *        indicating that the device is lost.
 *
 * \note  The VMK_SCSI_DEVICE_STATE_QUIESCED can only be used 
 *        during device unregistration time (with info set to
 *        VMK_SCSI_DEVICE_UNREGISTER). As part of this PSA
 *        will drain all I/Os and after this call no MPP device
 *        entry points will be invoked.
 *       
 * \param[in] device    Device whose state should be set.
 * \param[in] state     The new state of the device.
 * \param[in] info      Additional information about the state.
 *
 * \retval VMK_BAD_PARM   Incorrect state (e.g.
 *                        state = VMK_SCSI_DEVICE_STATE_OFF) or info param.
 *
 * \retval VMK_EOPNOTSUPP Setting state not supported (e.g
 *                        VMK_SCSI_DEVICE_STATE_APD).
 *         Others maybe added in the future.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiSetDeviceState(
   vmk_ScsiDevice *device,
   vmk_ScsiDeviceState state,
   vmk_ScsiDeviceStateInfo info);

/*
 ***********************************************************************
 * vmk_ScsiDeviceStateToString --                                 */ /**
 *
 * \ingroup MPP
 *
 * \brief Convert a device state into a human readable text string.
 *
 * \see vmk_ScsiSetDeviceState().
 *
 * \note This is a non-blocking call. 
 * 
 * \note Spin locks can be held while calling into this function
 *
 * \note The string output of this function is not recommended for 
 *       display in end-user visible tools. However, the string output 
 *       of this function is suitable for kernel logging.
 *
 * \param[in] state  Device state to convert to string.
 *
 * \return String with human readable representation of device state.
 *
 *
 ***********************************************************************
 */
const char *vmk_ScsiDeviceStateToString(
   vmk_ScsiDeviceState state);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceState --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the current device state.
 *
 * \see vmk_ScsiSetDeviceState().
 * \see vmk_ScsiDeviceStateToString().
 *
 * \note The purpose is to have a common device state for logging
 *       purposes.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device    Device whose state to acquire.
 *
 * \return Device's state
 *
 ***********************************************************************
 */
vmk_ScsiDeviceState vmk_ScsiGetDeviceState(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiDeviceIsPerenniallyReserved--                          */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the current perennial reservation state of the device.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device                Device whose perennial reservation 
 *                                  state to acquire. 
 * \param[out] perenniallyReserved  Device perennial reservation state. 
 *                                  Valid only if return status is VMK_OK.
 *
 * \retval VMK_OK        Device perennial reservation state obtained 
 *                       successfully.
 * \retval VMK_BAD_PARAM Passed in device OR perenniallyReserved 
 *                       parameter is NULL.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiDeviceIsPerenniallyReserved(
   vmk_ScsiDevice *device,
   vmk_Bool *perenniallyReserved);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceClass --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the current device class.
 *
 * This function returns the device class of the device. 
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device    Device whose class to acquire.
 *
 * \return Device class
 *
 ***********************************************************************
 */
vmk_ScsiDeviceClass vmk_ScsiGetDeviceClass(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceNumBlocks --                                  */ /**
 *
 * \ingroup MPP
 *
 * \brief Return the number of blocks as reported by Read Capacity.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device    Device whose # of blocks to acquire.
 *
 * \return The number of blocks.
 *
 ***********************************************************************
 */
vmk_uint64 vmk_ScsiGetDeviceNumBlocks(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceBlockSize --                                  */ /**
 *
 * \ingroup MPP
 *
 * \brief Return the block size as reported by Read Capacity.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device    Device whose block size to acquire.
 *
 * \return The block size used by the device.
 *
 ***********************************************************************
 */
vmk_ByteCountSmall vmk_ScsiGetDeviceBlockSize(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceMaxQDepth --                                  */ /**
 *
 * \ingroup MPP
 *
 * \brief Return the maximum number of I/Os queueable to the MPP by
 *        the framework.
 *
 * This function will get the queue depth of the logical device
 * registered with the PSA layer and will be the maximum number of
 * I/Os that is allowed to be outstanding to the logical device's
 * owning MP plugin.
 *
 * \see vmk_ScsiSetDeviceMaxQDepth().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This is \b not the maximum queue depth on the physical device.
 *
 * \note You may see less I/Os queued on the logical device even when
 *       multiple VMs are very busy on a VMFS volume. This is due to
 *       the throttling mechanism in ESX that ensure fairness.
 *
 * \param[in] device    Device to acquire max. queue depth for.
 *
 * \return Device's queue depth.
 *
 ***********************************************************************
 */
vmk_uint32 vmk_ScsiGetDeviceMaxQDepth(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiSetDeviceMaxQDepth --                                  */ /**
 *
 * \ingroup MPP
 *
 * \brief Set the maximum number of I/Os queuable to the MPP by
 *        the framework. 
 *
 * This function will set the queue depth of the logical device
 * registered with the PSA layer. It will be the maximum number of
 * I/Os that is allowed to be outstanding on the logical device.
 * A MPP plugin can use this to increase the number of I/Os if it
 * needs to distribute them on many paths (load balancing).
 *
 * \see vmk_ScsiGetDeviceMaxQDepth().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This is \b not the maximum queue depth on the physical device.
 *
 * \note You may see less I/Os queued on the logical device even when
 *       multiple VMs are very busy on a VMFS volume. This is due to
 *       the throttling mechanism in ESX that ensure fairness.
 *
 * \param[in]  device   Device whose max queueing depth to set.
 * \param[out] qDepth   Depth to set.
 *
 * \retval VMK_OK         The queue depth was successfully changed.
 * \retval VMK_BAD_PARAM  The qDepth parameter value was 0.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiSetDeviceMaxQDepth(
   vmk_ScsiDevice *device,
   vmk_uint32 qDepth);

/*
 ***********************************************************************
 * vmk_ScsiSwitchDeviceProbeRate --                               */ /**
 *
 * \ingroup MPP
 *
 * \brief Set a device's periodic path state update rate and
 *        return the current probe rate settings.
 *
 * \note This is a non-blocking call.
 * \note  The device should be probed at least as frequently as
 *        specified but may be probed more frequently than specified.
 *
 * \param[in]  vmkDevice              Targeted device.
 * \param[in]  newProbeRate           New probe rate
 *                                    ("DEFAULT" or "FAST").
 * \param[in]  flags                  Options if any
 *                                    (i.e. VMK_SCSI_ONE_PROBE_ONLY).
 * \param[out] currentProbeRate       Probe rate prior to this call.
 * \param[out] currentProbeRateFlags  Probe flags prior to this call.
 *
 * \retval VMK_OK Probe rate set successfully.
 * \retval VMK_BAD_PARAM A new probe rate other than
 *         VMK_SCSI_PROBE_RATE_DEFAULT or VMK_SCSI_PROBE_RATE_FAST
 *         was requested.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiSwitchDeviceProbeRate(
   vmk_ScsiDevice *vmkDevice,
   vmk_ScsiDeviceProbeRate newProbeRate,
   vmk_ScsiSetDeviceProbeRateOption flags,
   vmk_ScsiDeviceProbeRate *currentProbeRate,
   vmk_ScsiSetDeviceProbeRateOption *currentProbeRateFlags);

/*
 ***********************************************************************
 * vmk_ScsiGetDeviceName --                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the logical device's name.
 *
 * This function can be used to provide a consistent device name
 * among logs from PSA and MP plugins.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] device    Device to acquire name for.
 *
 * \return The name of the device as a human readable string.
 *
 ***********************************************************************
 */
const char *vmk_ScsiGetDeviceName(
   vmk_ScsiDevice *device);

/*
 ***********************************************************************
 * vmk_ScsiDeviceUIDAdd --                                        */ /**
 *
 * \ingroup MPP
 *
 * \brief Add a UID to a device.
 *
 * Valid UIDs (vmk_ScsiUid.id) are nul-terminated C strings that
 * may contain only the following characters:
 * - '0' through '9'
 * - 'a' through 'z'
 * - 'A' through 'Z'
 * - '_', ':', ',', '.'
 *
 * Valid UIDs should be persistent across power cycles, HBA
 * reordering, and SAN reconfiguration.  Additionally, a valid UID
 * should be the same on all ESX hosts that can access the same
 * physical storage. This function can only be called on a registered
 * device. Only one UID of type Primary ID is permitted.
 *
 * \note This function can only be called for registered devices.
 * \note This is a non-blocking call.
 * \note This call should only be used by MP plugins. 
 *
 * \param[in] vmkDevice    Device to add uid to.
 * \param[in] uidToAdd     UID to add to device.
 *
 * \retval VMK_OK                The UID was successfully added.
 * \retval VMK_EXISTS            The UID matches the uid of a
 *                               different device.
 * \retval VMK_BAD_PARAM         The UID has invalid flags or is
 *                               composed of disallowed characters.
 * \retval VMK_DUPLICATE_UID     The UID to be added to the device is
 *                               already associated with the device.
 * \retval VMK_TOO_MANY_ELEMENTS UID to be added is a primary UID and
 *                               the device already has a primary UID.
 * \retval VMK_NOT_SUPPORTED     The specified device is unregistered.
 * \retval VMK_NAME_TOO_LONG     UID is too long (>128 bytes).
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiDeviceUIDAdd(
   vmk_ScsiDevice *vmkDevice, 
   vmk_ScsiUid *uidToAdd);

/*
 ***********************************************************************
 * vmk_ScsiDeviceUIDRemove --                                     */ /**
 *
 * \ingroup MPP
 *
 * \brief  Remove a UID from a device.
 *
 * \see vmk_ScsiDeviceUIDAdd().
 *
 * \note This is a non-blocking call.
 * \note This call should only be used by MP plugins. 
 *
 * \param[in] device       Device to remove UID from.
 * \param[in] uidToRemove  UID to remove from device.
 *
 * \retval VMK_OK             The UID was successfully removed.
 * \retval VMK_READ_ONLY      The UID is the primary uid or a legacy UID
 *                            for a device and cannot be removed.
 * \retval VMK_NOT_FOUND      The UID is not associated with the device.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiDeviceUIDRemove(
   vmk_ScsiDevice *device,
   vmk_ScsiUid *uidToRemove);

/*
 ***********************************************************************
 * vmk_ScsiGetPathState--                                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Return current path state.
 *
 * This function returns the current path state. It does not block
 * nor does it acquire another spin lock. So it is safe to call it
 * while holding a spin lock without having to worry about blocking
 * or lock ranking issues.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to get state for.
 *
 * \return The state of the path.
 *
 ***********************************************************************
 */
vmk_ScsiPathState
vmk_ScsiGetPathState(vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiSetPathState--                                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Set PSA framework current path state.
 *
 * This function may only be called by the MP Plugin that is
 * managing the path. Other callers wishing to change a path's
 * state should instead use vmk_ScsiSetPluginPathState(). This
 * function exists so that an MP Plugin may notify the PSA
 * framework of a path state change. vmk_ScsiSetPluginPathState()
 * exists so that other callers can notify the MP Plugin of a
 * path state change (with the expectation that the MP Plugin will
 * duly call vmk_ScsiSetPathState() after doing any internal
 * bookkeeping).
 *
 * \see vmk_ScsiGetPathState().
 * \see vmk_ScsiSetPluginPathState().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] plugin    Plugin that owns the path.
 * \param[in] path      Path to set state on.
 * \param[in] state     State to set.
 *
 * \retval VMK_OK             The path state was successfully set.
 * \retval VMK_NO_PERMISSION  The plugin is not the owner of the path.
 * \retval VMK_BAD_PARAM      The path state was invalid.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiSetPathState(
   vmk_ScsiPlugin *plugin,
   vmk_ScsiPath *path,
   vmk_ScsiPathState state);

/*
 ***********************************************************************
 * vmk_ScsiSetPluginPathState--                                   */ /**
 *
 * \ingroup MPP
 *
 * \brief Set MP Plugin current path state.
 *
 * This function may only be called by non-MP Plugin callers. An
 * MP Plugin wishing to change a path's state should instead use
 * vmk_ScsiSetPathState().  This function exists so that
 * non-MP Plugin callers can notify a plugin of a path state
 * change.  vmk_ScsiSetPluginState() exists so that an MP Plugin
 * can notify the PSA framework of a path state change, with the
 * expectation that the MP Plugin will duly call
 * vmk_ScsiSetPathState() after its internal bookkeeping as a
 * of vmk_ScsiSetPluginPathState(). The path state change is
 * limited to admistrative change only to enable a disabled path
 * and vice versa.
 *
 * \see vmk_ScsiSetPathState().
 *
 * \note This is a blocking call.
 * \note Only VMK_SCSI_PATH_STATE_{ON/OFF} are allowed.
 *
 * \param[in] path   Path to set state on.
 * \param[in] state  State to set.
 *
 * \retval VMK_OK         Path state was successfully set.
 * \retval VMK_BAD_PARAM  Invalid path state was passed.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiSetPluginPathState(
   vmk_ScsiPath *path,
   vmk_ScsiPathState state);

/*
 ***********************************************************************
 * vmk_ScsiPathStateToString--                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Get a text string describing the current path state.
 *
 * \see vmk_ScsiGetPathState().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] state  State to convert to string.
 *
 * \return Human-readable string describing the path state or
 *         "Unknown path state" in case an invalid path state is passed.
 *
 ***********************************************************************
 */
const char *vmk_ScsiPathStateToString(
   vmk_ScsiPathState state);

/*
 ***********************************************************************
 * vmk_ScsiGetPathLUN --                                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Get path LUN.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to acquire logical unit number.
 *
 * \return LUN for the given path.
 *
 ***********************************************************************
 */
vmk_int32 vmk_ScsiGetPathLUN(
   vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiGetPathTarget --                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Get path target.
 *
 * \note This is the target ID as returned from the driver layer and
 *       in case of FC/iSCSI this is thus a logical mapping of the
 *       real target ID (which is a WWNN or IQN).
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to acquire target id for.
 *
 * \return Target ID for the given path.
 *
 ***********************************************************************
 */
vmk_int32 vmk_ScsiGetPathTarget(
   vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiGetPathChannel --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Get path Channel.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to acquire channel number.
 *
 * \return Channel number for the given path.
 *
 ***********************************************************************
 */
vmk_int32 vmk_ScsiGetPathChannel(
   vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiGetPathAdapter --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Get path's Adapter name.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] path   Path to acquire adapter name for.
 *
 * \return Adapter name for the given path.
 *
 ***********************************************************************
 */
const char *vmk_ScsiGetPathAdapter(
   vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiGetAdapterTransport --                                 */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the adapter transport type.
 *
 * \note This is a non-blocking call.
 *
 * \param[in]  adapName          Adapter to acquire transport from.
 * \param[out] transportType     Transport type
 *
 * \retval VMK_OK         Successfully obtained transport type.
 * \retval VMK_NOT_FOUND  The passed adapter could not be found.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetAdapterTransport(
   const vmk_Name *adapName,
   vmk_StorageTransport *transportType);

/*
 ***********************************************************************
 * vmk_ScsiAdapterTransportToString --                            */ /**
 *
 * \ingroup MPP
 *
 * \brief Convert adapter transport type into a human readable text string
 *
 * Get printable string for adapter transport type (fc/iscsi/..).
 *
 * \see vmk_ScsiGetAdapterTransport().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in]  adapterTransport Adapter Transport type
 * \param[out] transportString  Human readable string format
 *
 * \retval VMK_BAD_PARAM      Bad adapterTransport type
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiAdapterTransportToString(
   vmk_StorageTransport adapterTransport,
   const char **transportString); 

/*
 ***********************************************************************
 * vmk_ScsiGetAdapterPendingCmdInfo --                            */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the queued and active cmd counts for the adapter.
 *
 * Get the number of active and queued commands on the given adapter.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This information is only a snapshot of the values and they
 *       may even have changed as the function returns.
 *
 * \param[in]  adapterName    Adapter name.
 * \param[out] ioCmdCounts    If this pointer is non-NULL the memory
 *                            specified will be filled in with a
 *                            snapshot of the adapter active / queued
 *                            command count structure
 *                            \em vmk_ScsiIOCmdCounts. 
 * \param[out] queueDepthPtr  If the pointer is non-NULL the memory
 *                            specified will be filled in with a
 *                            snapshot of the adapter maximum queue
 *                            depth.
 *
 * \retval VMK_OK         Successfully obtained the pending cmd. info.
 * \retval VMK_NOT_FOUND  The passed adapter could not be found.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetAdapterPendingCmdInfo(
   const vmk_Name *adapterName,
   vmk_ScsiIOCmdCounts *ioCmdCounts,
   vmk_int32  *queueDepthPtr);

/*
 ***********************************************************************
 * vmk_ScsiGetPathInquiry --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the path's cached inquiry data.
 *
 * This function is used to obtain a copy of cached inquiry data for
 * a path. The call can fail if the data has not yet been obtained.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note \em inqBuf may be NULL in which case only \em *pageLen is
 *       returned. This can be used to find the right buffer size up
 *       front and see whether the data is available at all.
 * 
 * \param[in]  path           Scsi path from which we are retrieving
 *                            inquiry data.
 * \param[out] inqBuf         Buffer to which inquiry data will be copied.
 * \param[in]  inqBufLen      Size of the inquiry buffer in bytes.
 * \param[in]  vmkScsiPage    Page type for the inquiry data to be
 *                            copied into the inquiry buffer.
 * \param[out] pageLen        Optional actual page length based on page
 *                            header.
 *
 * \retval VMK_OK             Successfully obtained the inquiry data.
 * \retval VMK_NOT_SUPPORTED  The data was not yet available or the
 *                            page type was invalid.
 * 
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetPathInquiry(
   vmk_ScsiPath *path, 
   vmk_uint8 *inqBuf,
   vmk_ByteCountSmall inqBufLen,
   vmk_ScsiInqType vmkScsiPage,
   vmk_ByteCountSmall *pageLen);

/*
 ***********************************************************************
 * vmk_ScsiGetPathVendor --                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the path's vendor from its cached inquiry data.
 *
 * This function will retrieve the vendor string from the cached
 * inquiry data. 
 * 
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The passed string buffer must have space for at least
 *       VMK_SCSI_VENDOR_NAME_LENGTH characters.
 *
 * \param[in]  path     SCSI path from which we are retrieving inq
 *                      vendor.
 * \param[out] vendor   Buffer to which inquiry vendor will be copied.
 *
 ***********************************************************************
 */
void vmk_ScsiGetPathVendor(
   vmk_ScsiPath *path, 
   char vendor[VMK_SCSI_VENDOR_NAME_LENGTH]);

/*
 ***********************************************************************
 * vmk_ScsiGetPathModel --                                        */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the path's model from its cached inquiry data.
 * 
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The passed string buffer must have space for at least
 *       VMK_SCSI_MODEL_NAME_LENGTH characters.
 *
 * \param[in]  path     Scsi path from which we are retrieving inquiry
 *                      model.
 * \param[out] model    Buffer to which inquiry model will be copied.
 *
 * \return Human readable string with the inquiry data's model.
 *
 ***********************************************************************
 */
void vmk_ScsiGetPathModel(
   vmk_ScsiPath *path, 
   char model[VMK_SCSI_MODEL_NAME_LENGTH]);

/*
 ***********************************************************************
 * vmk_ScsiGetPathName --                                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the current path name.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The obtained string should never be modified/freed.
 *
 * \param[in] path   Path whose name to acquire.
 *
 * \return Human readable string with the path's name.
 *
 ***********************************************************************
 */
const char *vmk_ScsiGetPathName(
   vmk_ScsiPath *path);

/*
 ***********************************************************************
 * vmk_ScsiGetPathInfo --                                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the path's adapter/channel/target/lun info.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in]  vmkPath              Path to get info for.
 * \param[out] adapterName          Adapter name for the given path.
 * \param[in]  adapterNameSize      Size of adapterName array in bytes.
 * \param[out] channel              Channel of the given path.
 * \param[out] target               Target id of the given path.
 * \param[out] lun                  LUN id of the given path.
 *
 * \retval VMK_OK          The path info was successfully obtained.
 * \retval VMK_BAD_PARAM   One of the input parameter(s) was NULL.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetPathInfo(
   vmk_ScsiPath *vmkPath,
   vmk_Name *adapterName,
   vmk_ByteCountSmall adapterNameSize,
   vmk_uint32 *channel,
   vmk_uint32 *target,
   vmk_uint32 *lun);

/*
 ***********************************************************************
 * vmk_ScsiGetPathPendingCmdInfo --                               */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the queued and active cmd counts for the path.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This information is only a snapshot.
 *
 * \param[in]  vmkPath        Path to get info for.
 * \param[out] ioCmdCounts    If this is non-NULL the memory specified
 *                            will be filled in with a snapshot of the
 *                            path active / queued command count
 *                            structure vmk_ScsiIOCmdCounts. 
 * \param[out] queueDepthPtr  If this is is non-NULL the memory specified
 *                            will be filled in with a snapshot of the
 *                            path's maximum queue depth.
 *
 * \retval VMK_OK          Successfully obtained the pending cmd. info.
 * \retval VMK_BAD_PARAM   The path was invalid or NULL.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiGetPathPendingCmdInfo(
   vmk_ScsiPath *vmkPath,
   vmk_ScsiIOCmdCounts *ioCmdCounts,
   vmk_int32  *queueDepthPtr);

/*
 ***********************************************************************
 * vmk_ScsiIssuePathTaskMgmt--                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a task management command on the specified path.
 *
 * This function issues a task management command to abort/reset one
 * or more outstanding I/Os. While a virtual reset will reset only
 * commands for the given world a device/LUN reset will reset all I/O
 * on the path. An abort is targeting a single I/O though that I/O may
 * have been split into several smaller ones and can thus cause several
 * I/Os to be aborted in the driver layer.
 *
 * Note that any I/Os aborted by the call will complete asynchronously
 * and may not have completed when the call returns. The abort/reset is
 * thus only meant as a mean to speed up completion of I/Os - normally
 * by returning the I/O with an error (aborted/reset status).
 *
 * The \em taskMgmt structure is obtained from a prior call to
 * vmk_ScsiInitTaskMgmt().
 *
 * \note This is a blocking call.
 *
 * \param[in] path      Path to issue task mgmt on.
 * \param[in] taskMgmt  Task management request to issue.
 *
 * \retval VMK_OK         Task management call was successfull. This
 *                        does \em not mean that everything will
 *                        complete since there are natural races in the
 *                        stack so retry the operation if things don't
 *                        complete soon after this (in a second or two).
 * \retval VMK_BAD_PARAM  The task management type in the passed
 *                        taskMgmt struct is invalid.
 * \retval VMK_NO_MEMORY  Memory needed to issue the command could
 *                        not be allocated.
 * \retval VMK_FAILURE    Could not abort/reset for other reason,
 *                        but the operation can be retried.
 * \retval VMK_NOT_FOUND  The I/Os could not be found.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssuePathTaskMgmt(
   vmk_ScsiPath *path,
   vmk_ScsiTaskMgmt *taskMgmt);

/*
 ***********************************************************************
 * vmk_ScsiCreateCommand --                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Allocate and initialize a SCSI command.
 *
 * For performance reasons, this routine will initialize only 
 * some of the fields of the vmk_ScsiCommand (lba, lbc,
 * dataDirection, cdb, cdblen will NOT be initialized).
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The command must be freed using vmk_ScsiDestroyCommand().
 *
 * \return a pointer to vmk_ScsiCommand or NULL if allocation failed.
 *
 ***********************************************************************
 */
vmk_ScsiCommand *vmk_ScsiCreateCommand(void);

/*
 ***********************************************************************
 * vmk_ScsiDestroyCommand --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Free a SCSI command.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \pre The supplied command must have been allocated via
 *      vmk_ScsiCreateCommand() or one of the utility
 *      vmk_ScsiCreateXXXCommand() functions.
 *      command->sgArray must be NULL.  (the implication is that if
 *      this field was previously set, caller has vmk_SgFree()'d it).
 *
 * \param[in] command   Command to destroy.
 *
 ***********************************************************************
 */
void vmk_ScsiDestroyCommand(vmk_ScsiCommand *command);

/*
 ***********************************************************************
 * vmk_ScsiCreateInqCommand --                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Allocate and initialize a SCSI inquiry command.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The command must be freed using vmk_ScsiDestroyCommand().
 *
 * \return a pointer to vmk_ScsiCommand or NULL if allocation failed.
 *
 ***********************************************************************
 */
vmk_ScsiCommand *
vmk_ScsiCreateInqCommand(
   vmk_Bool evpd,
   vmk_uint8 evpdPage,
   vmk_uint16 minLen,
   vmk_uint16 maxLen);

/*
 ***********************************************************************
 * vmk_ScsiCreateTURCommand --                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Allocate and initialize a SCSI test unit ready command.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note The command must be freed using vmk_ScsiDestroyCommand().
 *
 * \return a pointer to vmk_ScsiCommand or NULL if allocation failed.
 *
 ***********************************************************************
 */
vmk_ScsiCommand *vmk_ScsiCreateTURCommand(void);


/*
 ***********************************************************************
 * vmk_ScsiGetNextDeviceCommand --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the next command for a logical device from the IO
 *        scheduler.
 *
 * This function is meant to be used from within an MPP in its
 * \em startCommand entry point. This should be called in a loop since
 * the scheduler will automatically stop when enough I/O has been
 * queued (e.g. the PSA queues may still hold I/Os, but due to max.
 * device queue depth or throttling no more I/Os will be issued).
 *
 * \note This is a non-blocking call.
 *
 * \note This call should only be used by MP plugins.
 *
 * \pre The caller should not hold any spinlocks.
 * \pre The caller should have acquired any needed resources that need
 *      to be tied to the I/O up front as I/Os cannot be returned to
 *      the device queue again.
 *
 * \param[in] vmkDev    Device for which to get the next command.
 *
 * \return Pointer to a SCSI command or NULL if there are no more
 *         commands to send at this time.
 *         In that case, SCSI will invoke the plugin's \em start() entry
 *         point again when the scheduler wants to issue I/Os again.
 *
 ***********************************************************************
 */ 
vmk_ScsiCommand *vmk_ScsiGetNextDeviceCommand(vmk_ScsiDevice *vmkDev);

/*
 ***********************************************************************
 * vmk_ScsiDeviceFlushAPDCommands --                              */ /**
 *
 * \ingroup MPP
 *
 * \note This is a non-blocking call.
 *
 * \brief Scan the device's scheduling queues for commands tagged with
 *        VMK_SCSI_COMMAND_FLAGS_NO_CONNECT_IF_APD and complete them
 *        with NO_CONNECT now.
 *
 * \param[in] vmkDev The target SCSI device
 *
 ***********************************************************************
 */
void vmk_ScsiDeviceFlushAPDCommands(vmk_ScsiDevice *vmkDev);

/*
 ***********************************************************************
 * vmk_ScsiIssueAsyncPathCommand --                               */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a command on the specified path.
 *
 * This function issues a SCSI command on a specific path. The command
 * will complete asynchronously at a later point using the command's
 * \em done() completion callback. If the specified path is claimed 
 * the function will call the owning MPP's \em pathIssueCmd entry point to
 * issue the command, otherwise the command is directly issued on the path.
 *
 * \note This is a non-blocking call.
 *
 * \pre The caller should not hold any spinlocks.
 *
 * \param[in] path      Path to issue command to.
 * \param[in] command   Command to issue on path.
 *
 * \retval VMK_OK          Successfully issued the command.
 * \retval VMK_NO_CONNECT  Path is in process of being removed.
 * \retval VMK_NO_MEMORY   Unable to allocate memory for additional
 *                         resources to be associated with command.
 * \retval VMK_TIMEOUT     The command had a timeout set and the
 *                         timeout time had already been passed when
 *                         the command was to be issued.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueAsyncPathCommand(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command);

/*
 ***********************************************************************
 * vmk_ScsiIssueAsyncPathCommandDirect --                         */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a command on the specified path.
 *
 * This function issues a SCSI command on a specific path. The command
 * will complete asynchronously at a later point using the command's
 * \em done() completion callback. The MPP's \em pathIssueCmd entry point
 * is not called.
 *
 * \note This is a non-blocking call.
 * \note This function should be called only by the owning MPP. 
 *
 * \pre The caller should not hold any spinlocks.
 *
 * \param[in] path      Path to issue command to.
 * \param[in] command   Command to issue on path.
 *
 * \retval VMK_OK          Successfully issued the command.
 * \retval VMK_NO_CONNECT  Path is in process of being removed.
 * \retval VMK_NO_MEMORY   Unable to allocate memory for additional
 *                         resources to be associated with command.
 * \retval VMK_TIMEOUT     The command had a timeout set and the
 *                         timeout time had already been passed when
 *                         the command was to be issued.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueAsyncPathCommandDirect(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command);

/*
 ***********************************************************************
 * vmk_ScsiIssueSyncPathCommand --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a synchronous command on the specified path.
 *
 * This function issues a command on a specific path and waits for
 * the command to complete before returning. If the specified path
 * is claimed by a MPP the function will call the owning MPP's 
 * \em pathIssueCmd entry point to issue the command, otherwise the
 * command is directly issued on the path.
 *
 * \see vmk_ScsiIssueSyncPathCommandWithData().
 * \see vmk_ScsiIssueSyncPathCommandWithRetries().
 *
 * \note This is a blocking call.
 * \note The call specifies the buffer to use for data transfer (if
 *       needed) using \em command->sgArray.
 * \note The calling world sleeps until the command completes.
 *
 * \pre The caller should not hold any spinlock.
 *
 * \param[in] path      Path to issue command to.
 * \param[in] command   Command to issue on path.
 *
 * \retval VMK_OK          The command was issued successfully and
 *                         the command's status is valid.
 * \retval VMK_NO_CONNECT  Path is in process of being removed
 * \retval VMK_NO_MEMORY   Unable to allocate memory for additional
 *                         resources associated with command.
 * \retval VMK_TIMEOUT     The command had a timeout set and was
 *                         already timed when it was to be issued.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueSyncPathCommand(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command);

/*
 ***********************************************************************
 * vmk_ScsiIssueSyncPathCommandWithData --                        */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a synchronous command on the specified path.
 *
 * This function issues a command on a specific path and waits for
 * the command to complete before returning. If the specified path
 * is claimed by a MPP the function will call the owning MPP's 
 * \em pathIssueCmd entry point to issue the command, otherwise the
 * command is directly issued on the path.
 * The caller can specify the buffer to use for data transfer using
 * the \em data and \em dataLen parameters.  The passed data buffer is
 * used to create an sgArray for the command, so the
 * \em command->sgArray must be NULL.
 *
 * \see vmk_ScsiIssueSyncPathCommand().
 * \see vmk_ScsiIssueSyncPathCommandWithRetries().
 *
 * \note This is a blocking call.
 * \note The calling world sleeps until the command completes.
 *
 * \pre The caller should not hold any spinlock.
 * \pre The \em command->sgArray must be NULL.
 *
 * \param[in]     path     Path to issue command to.
 * \param[in]     command  Command to issue on path.
 * \param[in,out] data     Data buffer.
 * \param[in]     dataLen  Length of data buffer.
 *
 * \retval VMK_OK          The command was issued successfully and
 *                         the command's status is valid.
 * \retval VMK_NO_CONNECT  Path is in process of being removed.
 * \retval VMK_NO_MEMORY   Unable to allocate memory for additional
 *                         resources associated with command.
 * \retval VMK_TIMEOUT     The command had a timeout set and was
 *                         already timed when it was to be issued.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueSyncPathCommandWithData(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command,
   void *data,
   vmk_ByteCountSmall dataLen);

/*
 ***********************************************************************
 * vmk_ScsiIssueSyncPathCommandWithRetries --                     */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a synchronous command on the specified path.
 *
 * This function issues a command on a specific path and waits for
 * the command to complete before returning. If the specified path
 * is claimed by a MPP the function will call the owning MPP's
 * \em pathIssueCmd entry point to issue the command, otherwise the
 * command is directly issued on the path.
 * This function is same as \em vmk_ScsiIssueSyncPathCommandWithData(),
 * the only difference being that it retries the command on all transient
 * errors. Refer \em vmkapi_scsi_ext.h to read about transient errors.
 *
 * \see vmk_ScsiIssueSyncPathCommand().
 * \see vmk_ScsiIssueSyncPathCommandWithData().
 *
 * \note This is a blocking call.
 * \note The calling world sleeps until the command completes.
 *
 * \param[in]  path              The path the cmd is to be issued to.
 * \param[in]  command           Command to be issued.
 * \param[in]  data              Read/write data associated with
 *                               the command.
 * \param[in]  dataLen           Length of the read/write data
 *                               associated with the command.
 *
 * \retval VMK_OK          The command was issued successfully and
 *                         the command's status is valid.
 * \retval VMK_NO_CONNECT  Path is in process of being removed.
 * \retval VMK_NO_MEMORY   Unable to allocate memory for additional
 *                         resources associated with command.
 * \retval VMK_TIMEOUT     The command had a timeout set and was
 *                         already timed when it was to be issued.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueSyncPathCommandWithRetries(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command,
   vmk_uint8 *data,
   vmk_ByteCountSmall dataLen);

/*
 ***********************************************************************
 * vmk_ScsiIssueSyncDumpCommand --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Issue a dump command to a path during a system core dump.
 *
 * This function issues a dump command on the given path and
 * busywaits until the command completes. It is meant for an MP plugin
 * to call this from it's device->dumpCommand entry point.
 *
 * \note This is a blocking call.
 * \note This does not call the MPP's \em pathIssueCmd entry point
 *
 * \param[in] path      Path to issue command to.
 * \param[in] command   Command to issue on path.
 *
 * \retval VMK_OK       Dump command was issued successfully and
 *                      the command's status is valid.
 * \retval VMK_FAILURE  The adapter backing the path is not enabled.
 * \retval VMK_TIMEOUT  Command timed out and should NOT be retried.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiIssueSyncDumpCommand(
   vmk_ScsiPath *path,
   vmk_ScsiCommand *command);

/*
 ***********************************************************************
 * vmk_ScsiIssueSyncFilterCommandWithRetries --                   */ /**
 *
 * \ingroup MPP
 * 
 * \brief Issue a synchronous command on a device.
 *
 * This function issues a command on the device on behalf of
 * the Filter or VAAI plugin and waits for the command to complete
 * before returning. The device is opened before command is issued, 
 * and is closed after the command completes. The caller can specify 
 * the buffer to use for any data transfer using either the data and 
 * dataLen parameters or vmkCmd->sgArray, but not both.
 * 
 * The command gets retried for basic low level failures such
 * as device busy, queue full, and some check conditions.
 *
 * \note Only Filters and VAAI plugins can use this API.
 * \note This is a blocking call.
 * \note The calling world sleeps until the command completes.
 *
 * \param[in]  vmkPlugin   Issuing plugin.
 *                         Has to be Filter or VAAI type. 
 * \param[in]  vmkDevice   The device the cmd is to be issued to.
 * \param[in]  vmkCmd      Command to be issued.
 * \param[in]  data        Read/write data associated with
 *                         the command.
 * \param[in]  dataLen     Length of the read/write data
 *                         associated with the command,
 *                         or NULL if vmkCmd->sgArray is used.
 *
 * \return The IO issue status
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiIssueSyncFilterCommandWithRetries(
   vmk_ScsiPlugin *vmkPlugin,
   struct vmk_ScsiDevice *vmkDevice,
   vmk_ScsiCommand *vmkCmd,
   vmk_uint8 *data,
   vmk_ByteCountSmall dataLen);

/*
 ***********************************************************************
 * vmk_ScsiRegisterEventHandler --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Add an Event Handler for the specific masked event types
 *        on the specified adapter.
 *
 * This function registers a handler that will be called as certain
 * events on the passed adapter occur.
 *
 * \see vmk_ScsiAdapterEvents.
 * \see vmk_ScsiUnRegisterEventHandler().
 *
 * \note This is a non-blocking call.
 * \note When the callback is called, it should  not grab any locks
 *       below minor rank 7, major rank SP_RANK_SCSI.
 *
 * \param[in] adapterName  Adapter name to register callback for.
 * \param[in] mask         Events to be notified of as a bit mask.
 * \param[in] handlerCbk   Callback to be called when an event
 *                         occurs.
 *
 * \retval VMK_OK               Event handler successfully registered.
 * \retval VMK_INVALID_ADAPTER  The passed adapter does not exist.
 * \retval VMK_NO_MEMORY        Failed to allocate memory for event.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiRegisterEventHandler(const vmk_Name *adapterName, vmk_uint32 mask,
      vmk_EventHandlerCbk handlerCbk);

/*
 ***********************************************************************
 * vmk_ScsiUnRegisterEventHandler --                              */ /**
 *
 * \ingroup MPP
 *
 * \brief Remove an Event Handler for the specific masked event types
 *    on the specified adapter.
 *
 * This function unregisters an event handler earlier registered by
 * vmk_ScsiRegisterEventHandler. All arguments need to match those that
 * were used to register the handler in order for the call to succeed.
 *
 * \see vmk_ScsiRegisterEventHandler().
 *
 * \note This is a non-blocking call.
 *
 * \param[in] adapterName  Name of the adapter to unregister for events.
 * \param[in] mask         Event bit mask to unregister.
 * \param[in] handlerCbk   Event callback to be unregistered.
 *
 * \retval VMK_OK               Event handler successfully unregistered.
 * \retval VMK_INVALID_ADAPTER  The passed adapter does not exist.
 *
 ***********************************************************************
 */
VMK_ReturnStatus
vmk_ScsiUnRegisterEventHandler(const vmk_Name *adapterName, vmk_uint32 mask,
      vmk_EventHandlerCbk handlerCbk);

/*
 ***********************************************************************
 * vmk_ScsiAllocatePlugin --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Allocate a plugin data structure.
 *
 * This function will allocate a plugin structure for use when
 * registering the plugin with PSA. The structure should be freed
 * with vmk_ScsiFreePlugin() again when it is no longer registered.
 *
 * \see vmk_ScsiRegisterPlugin().
 * \see vmk_ScsiUnregisterPlugin().
 * \see vmk_ScsiFreePlugin().
 *
 * \note This is a blocking call.
 *
 * \post The plugin MUST be freed through vmk_ScsiFreePlugin().
 *
 * \param[in] heapID       Heap id to allocate memory from.
 * \param[in] pluginName   Name of allocating plugin.
 *
 * \return Pointer to vmk_ScsiPlugin structure or NULL if memory
 *         could not be allocated for the structure.
 *
 ***********************************************************************
 */
vmk_ScsiPlugin *vmk_ScsiAllocatePlugin(
   vmk_HeapID heapID, char *pluginName);

/*
 ***********************************************************************
 * vmk_ScsiFreePlugin --                                          */ /**
 *
 * \ingroup MPP
 *
 * \brief Free a plugin data structure allocated by
 *        vmk_ScsiAllocatePlugin().
 *
 * \see vmk_ScsiAllocatePlugin().
 * \see vmk_ScsiUnregisterPlugin().
 *
 * \note This is a blocking call.
 *
 * \pre The plugin structure must first be unregistered with PSA (or
 *      never have been registered).
 *
 * \param[in] plugin    Plugin to free.
 *
 ***********************************************************************
 */
void vmk_ScsiFreePlugin(
   vmk_ScsiPlugin *plugin);

/*
 ***********************************************************************
 * vmk_ScsiRegisterPlugin --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Register a plugin with SCSI.
 *
 * This function will register a plugin with PSA.
 *
 * \see vmk_ScsiAllocatePlugin().
 * \see vmk_ScsiUnregisterPlugin().
 *
 * \note This is a blocking call.
 *
 * \pre The vmk_ScsiPlugin structure MUST have been allocated through
 *      vmk_ScsiAllocatePlugin().
 *
 * \param[in] plugin    Plugin to register.
 *
 * \retval VKM_OK             Successfully registered the plugin.
 * \retval VMK_EXISTS         The plugin is already registered with PSA.
 * \retval VMK_NOT_SUPPORTED  The API revision the plugin is compiled
 *                            for is incompatible with the ESX server.
 * \retval VMK_BAD_PARAM      The plugin's type is invalid/unsupported
 *                            or some required entry points are not set.
 * 
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiRegisterPlugin(
   vmk_ScsiPlugin *plugin);

/*
 ***********************************************************************
 * vmk_ScsiUnregisterPlugin --                                    */ /**
 *
 * \ingroup MPP
 *
 * \brief Unregister a plugin previously registered via
 *        vmk_ScsiRegisterPlugin().
 *
 * This function is used to unregister a plugin that has previously
 * been successfully registered with PSA. Before a plugin can be
 * unregistered it must not have any paths claimed or devices
 * registered.
 * The plugin must also have a state of VMK_SCSI_PLUGIN_STATE_DISABLED
 * when this function is called, which has to be set through
 * vmkScsiSetPluginState() - the latter will ensure that no claim
 * operations will be passed to the plugin.
 *
 * \see vmk_ScsiFreePlugin().
 * \see vmk_ScsiSetPluginState().
 *
 * \note This is a blocking call.
 *
 * \pre The plugin should have released all its SCSI resources (paths,
 *      devices, ...).
 * \pre The plugin state must be VMK_SCSI_PLUGIN_STATE_DISABLED.
 *
 * \param[in] plugin    Plugin to unregister.
 *
 * \retval VMK_OK         Successfully unregistered the plugin.
 * \retval VMK_BAD_PARAM  The plugin was already not registered.
 * \retval VMK_BUSY       The plugin state was not set to disabled.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiUnregisterPlugin(
   vmk_ScsiPlugin *plugin);

/*
 ***********************************************************************
 * vmk_ScsiSetPluginState--                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Set the current state of the plugin.
 *
 * This function sets the plugin state to one of the possible values
 * defined by the vmk_ScsiPluginState enum. The plugin will normally
 * operate in ENABLED mode and when the \em pathClaimBegin entry point
 * is called the plugin should (temporarily) set it to CLAIM_PATHS and
 * then set it back to ENABLED when the \em pathClaimEnd entry point is
 * later invoked. Lastly it should be set to DISABLED before the
 * plugin is finally unregistered.
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] plugin    Plugin to set state to.
 * \param[in] state     New state to set.
 *
 ***********************************************************************
 */
void vmk_ScsiSetPluginState(
   vmk_ScsiPlugin *plugin,
   vmk_ScsiPluginState state);

/*
 ***********************************************************************
 * vmk_ScsiGetPluginState--                                       */ /**
 *
 * \ingroup MPP
 *
 * \brief Get the current state of the plugin.
 *
 * \see vmk_ScsiSetPluginState().
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \param[in] plugin    Plugin to get state from.
 *
 ***********************************************************************
 */
vmk_ScsiPluginState vmk_ScsiGetPluginState(
   vmk_ScsiPlugin *plugin);

/*
 ***********************************************************************
 * vmk_ScsiIncReserveGeneration --                                */ /**
 *
 * \ingroup MPP
 *
 * \brief Increment the reservation generation count of the device.
 *
 * This function is used to tell the PSA layer that a reservation was
 * broken/cleared by the MP plugin and thus PSA should fail any
 * reservation sensitive transactions using the previous generation.
 *
 * The intent is that an MP plugin calls this if it has to do a failover
 * while a SCSI-2 reservation is held on the device and thus has to
 * force a LU reset on the new path (or if it has to do a LU reset for
 * any other reason - or invokes/knows about any other operation that
 * will clear or has cleared the held reservation).
 *
 * \note This is a non-blocking call.
 *
 * \note Spin locks can be held while calling into this function
 *
 * \note This call should only be used by MP plugins. 
 *
 * \param[in] vmkDevice    Device whose reservation generation to bump.
 *
 ***********************************************************************
 */
void
vmk_ScsiIncReserveGeneration(vmk_ScsiDevice *vmkDevice);

/*
 ***********************************************************************
 * vmk_ScsiCommandMaxCommands --                                  */ /**
 *
 * \ingroup MPP
 *
 * \brief Maximum outstanding SCSI commands
 *
 * This returns the maximum number of SCSI commands that the VMkernel
 * SCSI command slab allocator supports. These many SCSI commands can
 * be outstanding in the ESX kernel at any time.
 *
 * Any slab in a plugin, that is created for the purpose of allocating
 * any per-command plugin data structures, should be sized per this, to
 * ensure that at any time we have enough plugin objects for all the
 * SCSI commands.
 *
 ***********************************************************************
 */
vmk_uint32
vmk_ScsiCommandMaxCommands(void);

/*
 ***********************************************************************
 * vmk_ScsiCommandMaxFree --                                      */ /**
 *
 * \ingroup MPP
 *
 * \brief Maximum idle SCSI commands
 *
 * This returns the maximum number of SCSI commands that the SCSI command
 * allocator will keep in its cache for fast allocation. Any more freed
 * SCSI commands are freed to the slab heap, for better memory utilization.
 *
 * This should be used by plugins as a good guidance value while creating
 * their own slabs for any per-command object allocation.
 *
 ***********************************************************************
 */
vmk_uint32
vmk_ScsiCommandMaxFree(void);

/*
 ***********************************************************************
 * vmk_ScsiDefaultDeviceGetBoolAttr --                            */ /**
 *
 * \ingroup MPP
 *
 * \brief Determine if a device attribute is supported by a device.
 *
 * PSA invokes this function by default if the isSSD() and
 * isLocal() plugin-entry points are not specified.
 *
 * The behaviour for various device attributes is outlined below.
 *
 * - VMK_SCSI_DEVICE_BOOL_ATTR_PSEUDO
 *  - Device is not a pseudo-device. Always return VMK_FALSE in result.
 *
 * - VMK_SCSI_DEVICE_BOOL_ATTR_SSD
 *  - Issue a T10-standards-based EVPD inquiry (B1H - Block Device
 *    Characteristics VPD Page) to determine if the given device is SSD.
 *    SSD Devices report a medium rotation rate of 0x1 (non-rotating medium).
 *
 * - VMK_SCSI_DEVICE_BOOL_ATTR_LOCAL
 *  - Determine if the device is local based on storage transport type of
 *    the adapter associated with the device. Devices residing on the
 *    following storage transports are considered local by default.
 *   - VMK_STORAGE_ADAPTER_TRANSPORT_UNKNOWN
 *   - VMK_STORAGE_ADAPTER_BLOCK
 *   - VMK_STORAGE_ADAPTER_SATA
 *   - VMK_STORAGE_ADAPTER_USB
 *   - VMK_STORAGE_ADAPTER_IDE
 *   - VMK_STORAGE_ADAPTER_PSCSI
 *
 * \see vmk_ScsiDeviceOps
 *
 * \note This is a blocking call.
 *
 * \param[in]  device      SCSI Device to be probed.
 * \param[in]  attr        Device attribute to probe for.
 * \param[out] result      Flag indicating device supports the attribute.
 *                         Valid only if return status is VMK_OK.
 *
 * \retval VMK_OK            Device attribute status obtained successfully.
 * \retval VMK_NO_MEMORY     Internal memory allocation failure.
 * \retval VMK_BAD_PARAM     Invalid input device or attribute.
 *
 ***********************************************************************
 */
VMK_ReturnStatus vmk_ScsiDefaultDeviceGetBoolAttr(
   vmk_ScsiDevice *device,
   vmk_ScsiDeviceBoolAttribute attr,
   vmk_Bool *result);

#endif  /* _VMKAPI_MPP_H_ */
/** @} */
/** @} */