root/sys/dev/ocs_fc/ocs_scsi.h
/*-
 * Copyright (c) 2017 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 * OCS SCSI API declarations
 *
 */

#if !defined(__OCS_SCSI_H__)
#define __OCS_SCSI_H__

#include "ocs_ddump.h"
#include "ocs_mgmt.h"
#include "ocs_utils.h"

/* ocs_scsi_rcv_cmd() ocs_scsi_rcv_tmf() flags */
#define OCS_SCSI_CMD_DIR_IN             (1U << 0)
#define OCS_SCSI_CMD_DIR_OUT            (1U << 1)
#define OCS_SCSI_CMD_SIMPLE             (1U << 2)
#define OCS_SCSI_CMD_HEAD_OF_QUEUE      (1U << 3)
#define OCS_SCSI_CMD_ORDERED            (1U << 4)
#define OCS_SCSI_CMD_UNTAGGED           (1U << 5)
#define OCS_SCSI_CMD_ACA                (1U << 6)
#define OCS_SCSI_FIRST_BURST_ERR        (1U << 7)
#define OCS_SCSI_FIRST_BURST_ABORTED    (1U << 8)
#define OCS_SCSI_PRIORITY_MASK          0xf0000
#define OCS_SCSI_PRIORITY_SHIFT         16

/* ocs_scsi_send_rd_data/recv_wr_data/send_resp flags */
#define OCS_SCSI_LAST_DATAPHASE         (1U << 0)
#define OCS_SCSI_NO_AUTO_RESPONSE       (1U << 1)
#define OCS_SCSI_LOW_LATENCY            (1U << 2)

#define OCS_SCSI_WQ_STEERING_SHIFT      (16)
#define OCS_SCSI_WQ_STEERING_MASK       (0xf << OCS_SCSI_WQ_STEERING_SHIFT)
#define OCS_SCSI_WQ_STEERING_CLASS      (0 << OCS_SCSI_WQ_STEERING_SHIFT)
#define OCS_SCSI_WQ_STEERING_REQUEST    (1 << OCS_SCSI_WQ_STEERING_SHIFT)
#define OCS_SCSI_WQ_STEERING_CPU        (2 << OCS_SCSI_WQ_STEERING_SHIFT)

#define OCS_SCSI_WQ_CLASS_SHIFT         (20)
#define OCS_SCSI_WQ_CLASS_MASK          (0xf << OCS_SCSI_WQ_CLASS_SHIFT)
#define OCS_SCSI_WQ_CLASS(x)            ((x & OCS_SCSI_WQ_CLASS_MASK) << OCS_SCSI_WQ_CLASS_SHIFT)

#define OCS_SCSI_WQ_CLASS_LOW_LATENCY   (1)

/*!
 * @defgroup scsi_api_base SCSI Base Target/Initiator
 * @defgroup scsi_api_target SCSI Target
 * @defgroup scsi_api_initiator SCSI Initiator
 */

/**
 * @brief SCSI command response.
 *
 * This structure is used by target-servers to specify SCSI status and
 * sense data.  In this case all but the @b residual element are used. For
 * initiator-clients, this structure is used by the SCSI API to convey the
 * response data for issued commands, including the residual element.
 */
typedef struct {
        uint8_t scsi_status;                    /**< SCSI status */
        uint16_t scsi_status_qualifier;         /**< SCSI status qualifier */
        uint8_t *response_data;                 /**< pointer to response data buffer */
        uint32_t response_data_length;          /**< length of response data buffer (bytes) */
        uint8_t *sense_data;                    /**< pointer to sense data buffer */
        uint32_t sense_data_length;             /**< length of sense data buffer (bytes) */
        int32_t residual;                       /**< command residual (not used for target), positive value
                                                  *  indicates an underflow, negative value indicates overflow
                                                  */
        uint32_t response_wire_length;          /**< Command response length received in wcqe */
} ocs_scsi_cmd_resp_t;

/* Status values returned by IO callbacks */
typedef enum {
        OCS_SCSI_STATUS_GOOD = 0,
        OCS_SCSI_STATUS_ABORTED,
        OCS_SCSI_STATUS_ERROR,
        OCS_SCSI_STATUS_DIF_GUARD_ERROR,
        OCS_SCSI_STATUS_DIF_REF_TAG_ERROR,
        OCS_SCSI_STATUS_DIF_APP_TAG_ERROR,
        OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR,
        OCS_SCSI_STATUS_PROTOCOL_CRC_ERROR,
        OCS_SCSI_STATUS_NO_IO,
        OCS_SCSI_STATUS_ABORT_IN_PROGRESS,
        OCS_SCSI_STATUS_CHECK_RESPONSE,
        OCS_SCSI_STATUS_COMMAND_TIMEOUT,
        OCS_SCSI_STATUS_TIMEDOUT_AND_ABORTED,
        OCS_SCSI_STATUS_SHUTDOWN,
        OCS_SCSI_STATUS_NEXUS_LOST,

} ocs_scsi_io_status_e;

/* SCSI command status */
#define SCSI_STATUS_GOOD                                0x00
#define SCSI_STATUS_CHECK_CONDITION                     0x02
#define SCSI_STATUS_CONDITION_MET                       0x04
#define SCSI_STATUS_BUSY                                0x08
#define SCSI_STATUS_RESERVATION_CONFLICT                0x18
#define SCSI_STATUS_TASK_SET_FULL                       0x28
#define SCSI_STATUS_ACA_ACTIVE                          0x30
#define SCSI_STATUS_TASK_ABORTED                        0x40

/* Callback used by send_rd_data(), recv_wr_data(), send_resp() */
typedef int32_t (*ocs_scsi_io_cb_t)(ocs_io_t *io, ocs_scsi_io_status_e status, uint32_t flags,
        void *arg);

/* Callback used by send_rd_io(), send_wr_io() */
typedef int32_t (*ocs_scsi_rsp_io_cb_t)(ocs_io_t *io, ocs_scsi_io_status_e status, ocs_scsi_cmd_resp_t *rsp,
        uint32_t flags, void *arg);

/* ocs_scsi_cb_t flags */
#define OCS_SCSI_IO_CMPL                (1U << 0)       /* IO completed */
#define OCS_SCSI_IO_CMPL_RSP_SENT       (1U << 1)       /* IO completed, response sent */
#define OCS_SCSI_IO_ABORTED             (1U << 2)       /* IO was aborted */

/* ocs_scsi_recv_tmf() request values */
typedef enum {
        OCS_SCSI_TMF_ABORT_TASK = 1,
        OCS_SCSI_TMF_QUERY_TASK_SET,
        OCS_SCSI_TMF_ABORT_TASK_SET,
        OCS_SCSI_TMF_CLEAR_TASK_SET,
        OCS_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT,
        OCS_SCSI_TMF_LOGICAL_UNIT_RESET,
        OCS_SCSI_TMF_CLEAR_ACA,
        OCS_SCSI_TMF_TARGET_RESET,
} ocs_scsi_tmf_cmd_e;

/* ocs_scsi_send_tmf_resp() response values */
typedef enum {
        OCS_SCSI_TMF_FUNCTION_COMPLETE = 1,
        OCS_SCSI_TMF_FUNCTION_SUCCEEDED,
        OCS_SCSI_TMF_FUNCTION_IO_NOT_FOUND,
        OCS_SCSI_TMF_FUNCTION_REJECTED,
        OCS_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER,
        OCS_SCSI_TMF_SERVICE_DELIVERY,
} ocs_scsi_tmf_resp_e;

/**
 * @brief property names for ocs_scsi_get_property() functions
 */

typedef enum {
        OCS_SCSI_MAX_SGE,
        OCS_SCSI_MAX_SGL,
        OCS_SCSI_WWNN,
        OCS_SCSI_WWPN,
        OCS_SCSI_SERIALNUMBER,
        OCS_SCSI_PARTNUMBER,
        OCS_SCSI_PORTNUM,
        OCS_SCSI_BIOS_VERSION_STRING,
        OCS_SCSI_MAX_IOS,
        OCS_SCSI_DIF_CAPABLE,
        OCS_SCSI_DIF_MULTI_SEPARATE,
        OCS_SCSI_MAX_FIRST_BURST,
        OCS_SCSI_ENABLE_TASK_SET_FULL,
} ocs_scsi_property_e;

#define DIF_SIZE                8

/**
 * @brief T10 DIF operations
 *
 *      WARNING: do not reorder or insert to this list without making appropriate changes in ocs_dif.c
 */
typedef enum {
        OCS_SCSI_DIF_OPER_DISABLED,
        OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC,
        OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF,
        OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM,
        OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF,
        OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC,
        OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM,
        OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM,
        OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC,
        OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW,
} ocs_scsi_dif_oper_e;
#define OCS_SCSI_DIF_OPER_PASS_THRU     OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC
#define OCS_SCSI_DIF_OPER_STRIP         OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF
#define OCS_SCSI_DIF_OPER_INSERT        OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC

/**
 * @brief T10 DIF block sizes
 */
typedef enum {
        OCS_SCSI_DIF_BK_SIZE_512,
        OCS_SCSI_DIF_BK_SIZE_1024,
        OCS_SCSI_DIF_BK_SIZE_2048,
        OCS_SCSI_DIF_BK_SIZE_4096,
        OCS_SCSI_DIF_BK_SIZE_520,
        OCS_SCSI_DIF_BK_SIZE_4104
} ocs_scsi_dif_blk_size_e;

/**
 * @brief generic scatter-gather list structure
 */

typedef struct ocs_scsi_sgl_s {
        uintptr_t       addr;                   /**< physical address */
        uintptr_t       dif_addr;               /**< address of DIF segment, zero if DIF is interleaved */
        size_t          len;                    /**< length */
} ocs_scsi_sgl_t;

/**
 * @brief T10 DIF information passed to the transport
 */

typedef struct ocs_scsi_dif_info_s {
        ocs_scsi_dif_oper_e dif_oper;
        ocs_scsi_dif_blk_size_e blk_size;
        uint32_t ref_tag;
        uint32_t app_tag:16,
                check_ref_tag:1,
                check_app_tag:1,
                check_guard:1,
                dif_separate:1,

                /* If the APP TAG is 0xFFFF, disable checking the REF TAG and CRC fields */
                disable_app_ffff:1,

                /* if the APP TAG is 0xFFFF and REF TAG is 0xFFFF_FFFF, disable checking the received CRC field. */
                disable_app_ref_ffff:1,
                :10;
        uint64_t lba;
} ocs_scsi_dif_info_t;

/* Return values for calls from base driver to target-server/initiator-client */
#define OCS_SCSI_CALL_COMPLETE  0 /* All work is done */
#define OCS_SCSI_CALL_ASYNC     1 /* Work will be completed asynchronously */

/* Calls from target/initiator to base driver */

typedef enum {
        OCS_SCSI_IO_ROLE_ORIGINATOR,
        OCS_SCSI_IO_ROLE_RESPONDER,
} ocs_scsi_io_role_e;

extern void ocs_scsi_io_alloc_enable(ocs_node_t *node);
extern void ocs_scsi_io_alloc_disable(ocs_node_t *node);
extern ocs_io_t *ocs_scsi_io_alloc(ocs_node_t *node, ocs_scsi_io_role_e role);
extern void ocs_scsi_io_free(ocs_io_t *io);
extern ocs_io_t *ocs_io_get_instance(ocs_t *ocs, uint32_t index);

extern void ocs_scsi_register_bounce(ocs_t *ocs, void(*fctn)(void(*fctn)(void *arg), void *arg,
                                                             uint32_t s_id, uint32_t d_id, uint32_t ox_id));

/* Calls from base driver to target-server */

extern int32_t ocs_scsi_tgt_driver_init(void);
extern int32_t ocs_scsi_tgt_driver_exit(void);
extern int32_t ocs_scsi_tgt_io_init(ocs_io_t *io);
extern int32_t ocs_scsi_tgt_io_exit(ocs_io_t *io);
extern int32_t ocs_scsi_tgt_new_device(ocs_t *ocs);
extern int32_t ocs_scsi_tgt_del_device(ocs_t *ocs);
extern int32_t ocs_scsi_tgt_new_domain(ocs_domain_t *domain);
extern void ocs_scsi_tgt_del_domain(ocs_domain_t *domain);
extern int32_t ocs_scsi_tgt_new_sport(ocs_sport_t *sport);
extern void ocs_scsi_tgt_del_sport(ocs_sport_t *sport);
extern void ocs_scsi_sport_deleted(ocs_sport_t *sport);
extern int32_t ocs_scsi_validate_initiator(ocs_node_t *node);
extern int32_t ocs_scsi_new_initiator(ocs_node_t *node);
typedef enum {
        OCS_SCSI_INITIATOR_DELETED,
        OCS_SCSI_INITIATOR_MISSING,
} ocs_scsi_del_initiator_reason_e;
extern int32_t ocs_scsi_del_initiator(ocs_node_t *node, ocs_scsi_del_initiator_reason_e reason);
extern int32_t ocs_scsi_recv_cmd(ocs_io_t *io, uint64_t lun, uint8_t *cdb, uint32_t cdb_len, uint32_t flags);
extern int32_t ocs_scsi_recv_cmd_first_burst(ocs_io_t *io, uint64_t lun, uint8_t *cdb, uint32_t cdb_len, uint32_t flags,
        ocs_dma_t first_burst_buffers[], uint32_t first_burst_bytes);
extern int32_t ocs_scsi_recv_tmf(ocs_io_t *tmfio, uint64_t lun, ocs_scsi_tmf_cmd_e cmd, ocs_io_t *abortio,
        uint32_t flags);
extern ocs_sport_t *ocs_sport_get_instance(ocs_domain_t *domain, uint32_t index);
extern ocs_domain_t *ocs_domain_get_instance(ocs_t *ocs, uint32_t index);

/* Calls from target-server to base driver */

extern int32_t ocs_scsi_send_rd_data(ocs_io_t *io, uint32_t flags,
        ocs_scsi_dif_info_t *dif_info,
        ocs_scsi_sgl_t *sgl, uint32_t sgl_count,
        uint32_t wire_len, ocs_scsi_io_cb_t cb, void *arg);
extern int32_t ocs_scsi_recv_wr_data(ocs_io_t *io, uint32_t flags,
        ocs_scsi_dif_info_t *dif_info,
        ocs_scsi_sgl_t *sgl, uint32_t sgl_count,
        uint32_t wire_len, ocs_scsi_io_cb_t cb, void *arg);
extern int32_t ocs_scsi_send_resp(ocs_io_t *io, uint32_t flags, ocs_scsi_cmd_resp_t *rsp,
                ocs_scsi_io_cb_t cb, void *arg);
extern int32_t ocs_scsi_send_tmf_resp(ocs_io_t *io, ocs_scsi_tmf_resp_e rspcode, uint8_t addl_rsp_info[3],
                ocs_scsi_io_cb_t cb, void *arg);
extern int32_t ocs_scsi_tgt_abort_io(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg);
extern void ocs_scsi_io_complete(ocs_io_t *io);
extern uint32_t ocs_scsi_get_property(ocs_t *ocs, ocs_scsi_property_e prop);
extern void *ocs_scsi_get_property_ptr(ocs_t *ocs, ocs_scsi_property_e prop);

extern void ocs_scsi_del_initiator_complete(ocs_node_t *node);

extern void ocs_scsi_update_first_burst_transferred(ocs_io_t *io, uint32_t transferred);

/* Calls from base driver to initiator-client */

extern int32_t ocs_scsi_ini_driver_init(void);
extern int32_t ocs_scsi_ini_driver_exit(void);
extern int32_t ocs_scsi_ini_io_init(ocs_io_t *io);
extern int32_t ocs_scsi_ini_io_exit(ocs_io_t *io);
extern int32_t ocs_scsi_ini_new_device(ocs_t *ocs);
extern int32_t ocs_scsi_ini_del_device(ocs_t *ocs);
extern int32_t ocs_scsi_ini_new_domain(ocs_domain_t *domain);
extern void ocs_scsi_ini_del_domain(ocs_domain_t *domain);
extern int32_t ocs_scsi_ini_new_sport(ocs_sport_t *sport);
extern void ocs_scsi_ini_del_sport(ocs_sport_t *sport);
extern int32_t ocs_scsi_new_target(ocs_node_t *node);

typedef enum {
        OCS_SCSI_TARGET_DELETED,
        OCS_SCSI_TARGET_MISSING,
} ocs_scsi_del_target_reason_e;
extern int32_t ocs_scsi_del_target(ocs_node_t *node, ocs_scsi_del_target_reason_e reason);

/* Calls from the initiator-client to the base driver */

extern int32_t ocs_scsi_send_rd_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
        ocs_scsi_dif_info_t *dif_info,
        ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags);
extern int32_t ocs_scsi_send_wr_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
        ocs_scsi_dif_info_t *dif_info,
        ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags);
extern int32_t ocs_scsi_send_wr_io_first_burst(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
        ocs_scsi_dif_info_t *dif_info,
        ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst,
        ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags);
extern int32_t ocs_scsi_send_tmf(ocs_node_t *node, ocs_io_t *io, ocs_io_t *io_to_abort, uint64_t lun,
        ocs_scsi_tmf_cmd_e tmf, ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, ocs_scsi_rsp_io_cb_t cb, void *arg);
extern int32_t ocs_scsi_send_nodata_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len,
        ocs_scsi_rsp_io_cb_t cb, void *arg, uint32_t flags);
extern void ocs_scsi_del_target_complete(ocs_node_t *node);

typedef enum {
        OCS_SCSI_DDUMP_DEVICE,
        OCS_SCSI_DDUMP_DOMAIN,
        OCS_SCSI_DDUMP_SPORT,
        OCS_SCSI_DDUMP_NODE,
        OCS_SCSI_DDUMP_IO,
} ocs_scsi_ddump_type_e;

/* base driver to target/initiator */

struct ocs_scsi_vaddr_len_s {
        void *vaddr;
        uint32_t length;
} ;
extern int32_t ocs_scsi_get_block_vaddr(ocs_io_t *io, uint64_t blocknumber, ocs_scsi_vaddr_len_t addrlen[],
        uint32_t max_addrlen, void **dif_vaddr);

extern void ocs_scsi_ini_ddump(ocs_textbuf_t *textbuf, ocs_scsi_ddump_type_e type, void *obj);
extern void ocs_scsi_tgt_ddump(ocs_textbuf_t *textbuf, ocs_scsi_ddump_type_e type, void *obj);

/* Calls within base driver */
extern int32_t ocs_scsi_io_dispatch(ocs_io_t *io, void *cb);
extern int32_t ocs_scsi_io_dispatch_abort(ocs_io_t *io, void *cb);
extern void ocs_scsi_check_pending(ocs_t *ocs);

extern uint32_t ocs_scsi_dif_blocksize(ocs_scsi_dif_info_t *dif_info);
extern int32_t ocs_scsi_dif_set_blocksize(ocs_scsi_dif_info_t *dif_info, uint32_t blocksize);
extern int32_t ocs_scsi_dif_mem_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem);
extern int32_t ocs_scsi_dif_wire_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem);

uint32_t ocs_get_crn(ocs_node_t *node, uint8_t *crn, uint64_t lun);
void ocs_del_crn(ocs_node_t *node);
void ocs_reset_crn(ocs_node_t *node, uint64_t lun);

/**
 * @brief Notification from base driver that domain is in force-free path.
 *
 * @par Description Domain is forcefully going away.  Cleanup any resources associated with it.
 *
 * @param domain Pointer to domain being free'd.
 *
 * @return None.
 */

static inline void
ocs_scsi_notify_domain_force_free(ocs_domain_t *domain)
{
        /* Nothing to do */
        return;
}

/**
 * @brief Notification from base driver that sport is in force-free path.
 *
 * @par Description Sport is forcefully going away.  Cleanup any resources associated with it.
 *
 * @param sport Pointer to sport being free'd.
 *
 * @return None.
 */

static inline void
ocs_scsi_notify_sport_force_free(ocs_sport_t *sport)
{
        /* Nothing to do */
        return;
}

/**
 * @brief Notification from base driver that node is in force-free path.
 *
 * @par Description Node is forcefully going away.  Cleanup any resources associated with it.
 *
 * @param node Pointer to node being free'd.
 *
 * @return None.
 */

static inline void
ocs_scsi_notify_node_force_free(ocs_node_t *node)
{
        /* Nothing to do */
        return;
}
#endif /* __OCS_SCSI_H__ */