/*
* @file hv_drv_UsbStorageCommonScsi.h
* @brief Header file of usb scsi common.
*
* @author HiView SoC Software Team
* @version 1.0.0
* @date 2022-06-15
*/

#ifndef __HV_DRV_USB_STORAGE_COMMON_SCSI_H
#define __HV_DRV_USB_STORAGE_COMMON_SCSI_H

#include "hv_drv_UsbPortingTypes.h"
#include "hv_drv_UsbErrNo.h"

#define SCSI_UNKNOWN    0
#define SCSI_1          1
#define SCSI_1_CCS      2
#define SCSI_2          3
#define SCSI_3          4        /* SPC */
#define SCSI_SPC_2      5
#define SCSI_SPC_3      6

#define BLK_MAX_CDB     16

/*
 * The maximum number of SG segments that we will put inside a
 * scatterlist (unless chaining is used). Should ideally fit inside a
 * single page, to avoid a higher order allocation.  We could define this
 * to SG_MAX_SINGLE_ALLOC to pack correctly at the highest order.  The
 * minimum value is 32
 */
#define SCSI_MAX_SG_SEGMENTS        128

/*
 * Like SCSI_MAX_SG_SEGMENTS, but for archs that have sg chaining. This limit
 * is totally arbitrary, a setting of 2048 will get you at least 8mb ios.
 */
#define SCSI_MAX_SG_CHAIN_SEGMENTS  SCSI_MAX_SG_SEGMENTS

/*
 * DIX-capable adapters effectively support infinite chaining for the
 * protection information scatterlist
 */
#define SCSI_MAX_PROT_SG_SEGMENTS   0xFFFF

/*
 * Special value for scanning to specify scanning or rescanning of all
 * possible channels, (target) ids, or luns on a given shost.
 */
#define SCAN_WILD_CARD  ~0

/*
 *      SCSI opcodes
 */

#define TEST_UNIT_READY             0x00
#define REZERO_UNIT                 0x01
#define REQUEST_SENSE               0x03
#define FORMAT_UNIT                 0x04
#define READ_BLOCK_LIMITS           0x05
#define REASSIGN_BLOCKS             0x07
#define INITIALIZE_ELEMENT_STATUS   0x07
#define READ_6                      0x08
#define WRITE_6                     0x0a
#define SEEK_6                      0x0b
#define READ_REVERSE                0x0f
#define WRITE_FILEMARKS             0x10
#define SPACE                       0x11
#define INQUIRY                     0x12
#define RECOVER_BUFFERED_DATA       0x14
#define MODE_SELECT                 0x15
#define RESERVE                     0x16
#define RELEASE                     0x17
#define COPY                        0x18
#define ERASE                       0x19
#define MODE_SENSE                  0x1a
#define START_STOP                  0x1b
#define RECEIVE_DIAGNOSTIC          0x1c
#define SEND_DIAGNOSTIC             0x1d
#define ALLOW_MEDIUM_REMOVAL        0x1e

#define READ_FORMAT_CAPACITIES      0x23
#define SET_WINDOW                  0x24
#define READ_CAPACITY               0x25
#define READ_10                     0x28
#define WRITE_10                    0x2a
#define SEEK_10                     0x2b
#define POSITION_TO_ELEMENT         0x2b
#define WRITE_VERIFY                0x2e
#define VERIFY                      0x2f
#define SEARCH_HIGH                 0x30
#define SEARCH_EQUAL                0x31
#define SEARCH_LOW                  0x32
#define SET_LIMITS                  0x33
#define PRE_FETCH                   0x34
#define READ_POSITION               0x34
#define SYNCHRONIZE_CACHE           0x35
#define LOCK_UNLOCK_CACHE           0x36
#define READ_DEFECT_DATA            0x37
#define MEDIUM_SCAN                 0x38
#define COMPARE                     0x39
#define COPY_VERIFY                 0x3a
#define WRITE_BUFFER                0x3b
#define READ_BUFFER                 0x3c
#define UPDATE_BLOCK                0x3d
#define READ_LONG                   0x3e
#define WRITE_LONG                  0x3f
#define CHANGE_DEFINITION           0x40
#define WRITE_SAME                  0x41
#define UNMAP                       0x42
#define READ_TOC                    0x43
#define READ_HEADER                 0x44
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
#define LOG_SELECT                  0x4c
#define LOG_SENSE                   0x4d
#define XDWRITEREAD_10              0x53
#define MODE_SELECT_10              0x55
#define RESERVE_10                  0x56
#define RELEASE_10                  0x57
#define MODE_SENSE_10               0x5a
#define PERSISTENT_RESERVE_IN       0x5e
#define PERSISTENT_RESERVE_OUT      0x5f
#define VARIABLE_LENGTH_CMD         0x7f
#define REPORT_LUNS                 0xa0
#define SECURITY_PROTOCOL_IN        0xa2
#define MAINTENANCE_IN              0xa3
#define MAINTENANCE_OUT             0xa4
#define MOVE_MEDIUM                 0xa5
#define EXCHANGE_MEDIUM             0xa6
#define READ_12                     0xa8
#define WRITE_12                    0xaa
#define READ_MEDIA_SERIAL_NUMBER    0xab
#define WRITE_VERIFY_12             0xae
#define VERIFY_12                   0xaf
#define SEARCH_HIGH_12              0xb0
#define SEARCH_EQUAL_12             0xb1
#define SEARCH_LOW_12               0xb2
#define SECURITY_PROTOCOL_OUT       0xb5
#define READ_ELEMENT_STATUS         0xb8
#define SEND_VOLUME_TAG             0xb6
#define WRITE_LONG_2                0xea
#define EXTENDED_COPY               0x83
#define RECEIVE_COPY_RESULTS        0x84
#define ACCESS_CONTROL_IN           0x86
#define ACCESS_CONTROL_OUT          0x87
#define READ_16                     0x88
#define COMPARE_AND_WRITE           0x89
#define WRITE_16                    0x8a
#define READ_ATTRIBUTE              0x8c
#define WRITE_ATTRIBUTE             0x8d
#define VERIFY_16                   0x8f
#define SYNCHRONIZE_CACHE_16        0x91
#define WRITE_SAME_16               0x93
#define SERVICE_ACTION_IN           0x9e
/* values for service action in */
#define SAI_READ_CAPACITY_16        0x10
#define SAI_GET_LBA_STATUS          0x12
#define SAI_REPORT_REFERRALS        0x13
/* values for VARIABLE_LENGTH_CMD service action codes
 * see spc4r17 Section D.3.5, table D.7 and D.8 */
#define VLC_SA_RECEIVE_CREDENTIAL   0x1800
/* values for maintenance in */
#define MI_REPORT_IDENTIFYING_INFORMATION   0x05
#define MI_REPORT_TARGET_PGS        0x0a
#define MI_REPORT_ALIASES           0x0b
#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c
#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d
#define MI_REPORT_PRIORITY          0x0e
#define MI_REPORT_TIMESTAMP         0x0f
#define MI_MANAGEMENT_PROTOCOL_IN   0x10
/* value for MI_REPORT_TARGET_PGS ext header */
#define MI_EXT_HDR_PARAM_FMT        0x20
/* values for maintenance out */
#define MO_SET_IDENTIFYING_INFORMATION 0x06
#define MO_SET_TARGET_PGS           0x0a
#define MO_CHANGE_ALIASES           0x0b
#define MO_SET_PRIORITY             0x0e
#define MO_SET_TIMESTAMP            0x0f
#define MO_MANAGEMENT_PROTOCOL_OUT  0x10
/* values for variable length command */
#define XDREAD_32                   0x03
#define XDWRITE_32                  0x04
#define XPWRITE_32                  0x06
#define XDWRITEREAD_32              0x07
#define READ_32                     0x09
#define VERIFY_32                   0x0a
#define WRITE_32                    0x0b
#define WRITE_SAME_32               0x0d

/* Values for T10/04-262r7 */
#define ATA_16            0x85  /* 16-byte pass-thru */
#define ATA_12            0xa1  /* 12-byte pass-thru */

/*
 *  SCSI command lengths
 */

#define SCSI_MAX_VARLEN_CDB_SIZE 260

/*
 *  SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft
 *  T10/1561-D Revision 4 Draft dated 7th November 2002.
 */
#define SAM_STAT_GOOD            0x00
#define SAM_STAT_CHECK_CONDITION 0x02
#define SAM_STAT_CONDITION_MET   0x04
#define SAM_STAT_BUSY            0x08
#define SAM_STAT_INTERMEDIATE    0x10
#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14
#define SAM_STAT_RESERVATION_CONFLICT       0x18
#define SAM_STAT_COMMAND_TERMINATED         0x22    /* obsolete in SAM-3 */
#define SAM_STAT_TASK_SET_FULL   0x28
#define SAM_STAT_ACA_ACTIVE      0x30
#define SAM_STAT_TASK_ABORTED    0x40

#define DID_OK                   0x00    /* Transport good */
#define DID_NOT_CONNECT          0x01    /* Device has disappeared */
#define DID_CHECK_CONDITIONS     0x02    /* Interpret the sense data */
#define DID_IMM_RETRY            0x03    /* Retry without decrementing retry count */
#define DID_NEED_RETRY           0x04    /* Retry scsi command */
#define DID_ERROR                0x05    /* Internal error */
#define DID_ABORT                0x06    /* Told to abort for some other reason */

/*
 *  SENSE KEYS
 */
#define NO_SENSE            0x00
#define RECOVERED_ERROR     0x01
#define NOT_READY           0x02
#define MEDIUM_ERROR        0x03
#define HARDWARE_ERROR      0x04
#define ILLEGAL_REQUEST     0x05
#define UNIT_ATTENTION      0x06
#define DATA_PROTECT        0x07
#define BLANK_CHECK         0x08
#define COPY_ABORTED        0x0a
#define ABORTED_COMMAND     0x0b
#define VOLUME_OVERFLOW     0x0d
#define MISCOMPARE          0x0e

//
// SCSI-2 DEVICE TYPES
//
#define SCSI_DEVICE_DIRECT_ACCESS       0x00    // e.g. disk
#define SCSI_DEVICE_SEQUENTIAL_ACCESS   0x01
#define SCSI_DEVICE_PRINTER             0x02
#define SCSI_DEVICE_PROCESSOR           0x03
#define SCSI_DEVICE_WRITE_ONCE          0x04
#define SCSI_DEVICE_CDROM               0x05
#define SCSI_DEVICE_SCANNER             0x06
#define SCSI_DEVICE_OPTICAL             0x07
#define SCSI_DEVICE_MEDIUM_CHANGER      0x08
#define SCSI_DEVICE_COMM                0x09
#define SCSI_DEVICE_UNKNOWN             0x1F

//
// SCSI-2 MEDIUM TYPES
//
#define SCSI_MEDIUM_UNKNOWN     0xFF

//
// SCSI-2 SENSE KEYS
//
#define SENSE_NONE              0x00
#define SENSE_RECOVERED_ERROR   0x01
#define SENSE_NOT_READY         0x02
#define SENSE_MEDIUM_ERROR      0x03
#define SENSE_HARDWARE_ERROR    0x04
#define SENSE_ILLEGAL_REQUEST   0x05
#define SENSE_UNIT_ATTENTION    0x06
#define SENSE_DATA_PROTECT      0x07
#define SENSE_BLANK_CHECK       0x08
//...

//
// SCSI-2 ASC (of interest)
//
#define ASC_LUN                     0x04
#define ASC_INVALID_COMMAND_FIELD   0x24
#define ASC_MEDIA_CHANGED           0x28
#define ASC_RESET                   0x29
#define ASC_COMMANDS_CLEARED        0x2F
#define ASC_MEDIUM_NOT_PRESENT      0x3A

typedef enum {
    DIRECT_DATA_OUT = 0x0,
    DIRECT_DATA_IN = 0x01,
    DIRECT_NONE,
} DIRECTIONS;

struct scsi_data_buffer {
    u8 *buf;
    unsigned int length;
    unsigned int resid;
};

struct scsi_cmnd {
    struct scsi_device *device;
    DIRECTIONS sc_data_direction;
    u32 timeout;
    u32 length;
    u8 *cmnd;
    u16 cmd_len;

    u8  *sense_buffer;
    u32 sense_actual_length;

    struct scsi_data_buffer sdb;

    unsigned int underflow; /* Return error if less than
                   this amount is transferred */

    unsigned int transfersize;

    u32 result;
    unsigned char prot_op;
};

struct scsi_timeouts {
    u32 inquiry;
    u32 read_sector;
    u32 write_sector;
    u32 cmd_time;
    u32 unit_attn_repeat;
    u32 request_sense;
};

struct scsi_sense_hdr {
    /* See SPC-3 section 4.5 */
    u8 response_code;   /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
    u8 sense_key;
    u8 asc;
    u8 ascq;
    u8 byte4;
    u8 byte5;
    u8 byte6;
    u8 additional_length;   /* always 0 for fixed sense format */
};

struct disk_info {
    u64 di_total_sectors;
    u32 di_bytes_per_sect;
    u32 di_cylinders;
    u32 di_heads;
    u32 di_sectors;
    u32 di_flags;       // see definitions below
};

// device flags
struct device_flag {
    u8 rmb : 1;               // removable media bit (RMB)
    u8 medium_present : 1;    // a medium is present
    u8 fsdmounted : 1;        // is FSD mounted, deprecated
    u8 write_protected : 1;   // medium/device is write-protected
    u8 open : 1;              // device is open (DSK_Xxx)
    u8 device_removed : 1;    // device marked for removal; only set by UsbDiskAttach/UsbDiskDetach
    u8 powered_down : 1;      // device marked for suspend; only set by DSK_PowerDown
    u8 connected:1;           // device connected or not
    u8 medium_changed : 1;    // medium has changed
    u8 prevmedium_status : 1; // previous medium changed status
};

struct scsi_device;
struct scsi_ops {
    int (*inquiry) (struct scsi_device *dev);
    int (*read_capacity) (struct scsi_device *dev);
    int (*read_10) (struct scsi_device *dev, u8 *buf, u32 start_sec,
                    u32 size, u32 *actxfer);
    int (*write_10) (struct scsi_device *dev, u8 *buf, u32 start_sec,
                     u32 size, u32 *actxfer);
    int (*test_unit_ready) (struct scsi_device *dev);
    void (*fill_request_sense) (struct scsi_device *, struct scsi_cmnd *, u8);
};

/*
 * struct scsi_device
 * @extra: extra data, for usb storage data only
 * @priv: private data, for own block device
 */
struct scsi_device {
    char scsi_level;
    void *extra;
    void *priv;

    u8 vendor_id[8];
    u8 product_id[16];
    u8 product_rev[4];

    u32 id;
    u32 refs;
    u8 device_type;
    u8 lun;
    u8 media_type;

    u8 expecting_cc_ua:1;
    u8 expecting_lun_change:1;

    struct device_flag  flags;
    struct disk_info    disk_info;
    struct scsi_ops *ops;
    struct scsi_timeouts *timeouts;
#define SCSI_SENSE_BUFFERSIZE   96
    u8  sense_buffer[SCSI_SENSE_BUFFERSIZE];
};

INLINE
void scsi_set_extra(struct scsi_device *dev, void *data) {
    dev->extra = data;
}

INLINE
void *scsi_get_extra(struct scsi_device *dev) {
    return dev->extra;
}

INLINE
unsigned scsi_bufflen(struct scsi_cmnd *cmd) {
    return cmd->sdb.length;
}

INLINE
void scsi_set_resid(struct scsi_cmnd *cmd, int resid) {
    cmd->sdb.resid = resid;
}

INLINE
int scsi_get_resid(struct scsi_cmnd *cmd) {
    return cmd->sdb.resid;
}

#endif /* __COMMON_SCSI_H */
