root/headers/private/drivers/scsi_cmds.h
/*
 * Copyright 2004-2015, Haiku, Inc. All RightsReserved.
 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
 *
 * Distributed under the terms of the MIT License.
 */
#ifndef _SCSI_CMDS_H
#define _SCSI_CMDS_H


//! SCSI commands and their data structures and constants


#include <lendian_bitfield.h>


// Always keep in mind that SCSI is big-endian!

#define SCSI_STD_TIMEOUT 10

// SCSI device status (as the result of a command)
#define SCSI_STATUS_GOOD                                        (0 << 1)
#define SCSI_STATUS_CHECK_CONDITION                     (1 << 1)        // error occured
#define SCSI_STATUS_CONDITION_MET                       (2 << 1)
        // "found" for SEARCH DATA and PREFETCH
#define SCSI_STATUS_BUSY                                        (4 << 1)
        // try again later (??? == QUEUE_FULL ???)
#define SCSI_STATUS_INTERMEDIATE                        (8 << 1)
        // used by linked command only
#define SCSI_STATUS_INTERMEDIATE_COND_MET       (10 << 1)       // ditto
#define SCSI_STATUS_RESERVATION_CONFLICT        (12 << 1)
        // only if RESERVE/RELEASE is used
#define SCSI_STATUS_COMMAND_TERMINATED          (17 << 1)
        // aborted by TERMINATE I/O PROCESS
#define SCSI_STATUS_QUEUE_FULL                          (20 << 1)       // queue full

#define SCSI_STATUS_MASK 0xfe

// SCSI sense key
#define SCSIS_KEY_NO_SENSE                                      0
#define SCSIS_KEY_RECOVERED_ERROR                       1
#define SCSIS_KEY_NOT_READY                                     2
        // operator intervention may be required
#define SCSIS_KEY_MEDIUM_ERROR                          3
        // can be set if source could be hardware error
#define SCSIS_KEY_HARDWARE_ERROR                        4
#define SCSIS_KEY_ILLEGAL_REQUEST                       5       // invalid command
#define SCSIS_KEY_UNIT_ATTENTION                        6
        // medium changed or target reset
#define SCSIS_KEY_DATA_PROTECT                          7       // data access forbidden
#define SCSIS_KEY_BLANK_CHECK                           8
        // tried to read blank or to write non-blank medium
#define SCSIS_KEY_VENDOR_SPECIFIC                       9
#define SCSIS_KEY_COPY_ABORTED                          10
        // error in COPY or COMPARE command
#define SCSIS_KEY_ABORTED_COMMAND                       11
        // aborted by target, retry *may* help
#define SCSIS_KEY_EQUAL                                         12      // during SEARCH: data found
#define SCSIS_KEY_VOLUME_OVERFLOW                       13
        // tried to write buffered data beyond end of medium
#define SCSIS_KEY_MISCOMPARE                            14
#define SCSIS_KEY_RESERVED                                      15

// SCSI ASC and ASCQ data - (ASC << 8) | ASCQ
// all codes with bit 7 of ASC or ASCQ set are vendor-specific
#define SCSIS_ASC_NO_SENSE                                      0x0000
#define SCSIS_ASC_IO_PROC_TERMINATED            0x0006
#define SCSIS_ASC_AUDIO_PLAYING                         0x0011
#define SCSIS_ASC_AUDIO_PAUSED                          0x0012
#define SCSIS_ASC_AUDIO_COMPLETED                       0x0013
#define SCSIS_ASC_AUDIO_ERROR                           0x0014
        // playing has stopped due to error
#define SCSIS_ASC_AUDIO_NO_STATUS                       0x0015
#define SCSIS_ASC_NO_INDEX                                      0x0100  // no index/sector signal
#define SCSIS_ASC_NO_SEEK_CMP                           0x0200  // ???
#define SCSIS_ASC_WRITE_FAULT                           0x0300
#define SCSIS_ASC_LUN_NOT_READY                         0x0400
        // LUN not ready, cause not reportable
#define SCSIS_ASC_LUN_BECOMING_READY            0x0401
        // LUN in progress of becoming ready
#define SCSIS_ASC_LUN_NEED_INIT                         0x0402
        // LUN need initializing command
#define SCSIS_ASC_LUN_NEED_MANUAL_HELP          0x0403
        // LUN needs manual intervention
#define SCSIS_ASC_LUN_FORMATTING                        0x0404
        // LUN format in progress
#define SCSIS_ASC_LUN_SEL_FAILED                        0x0500
        // LUN doesn't respond to selection
#define SCSIS_ASC_LUN_COM_FAILURE                       0x0800  // LUN communication failure
#define SCSIS_ASC_LUN_TIMEOUT                           0x0801
        // LUN communication time-out
#define SCSIS_ASC_LUN_COM_PARITY                        0x0802
        // LUN communication parity failure
#define SCSIS_ASC_LUN_COM_CRC                           0x0803
        // LUN communication CRC failure (SCSI-3)
#define SCSIS_ASC_WRITE_ERR_AUTOREALLOC         0x0c01
        // recovered by auto-reallocation
#define SCSIS_ASC_WRITE_ERR_AUTOREALLOC_FAILED 0x0c02
#define SCSIS_ASC_ECC_ERROR                                     0x1000
#define SCSIS_ASC_UNREC_READ_ERR                        0x1100  // unrecovered read error
#define SCSIS_ASC_READ_RETRIES_EXH                      0x1101  // read retries exhausted
#define SCSIS_ASC_UNREC_READ_ERR_AUTOREALLOC_FAILED 0x1104
        // above + auto-reallocate failed
#define SCSIS_ASC_RECORD_NOT_FOUND                      0x1401
#define SCSIS_ASC_RANDOM_POS_ERROR                      0x1500  // random positioning error
#define SCSIS_ASC_POSITIONING_ERR                       0x1501
        // mechanical positioning error
#define SCSIS_ASC_POS_ERR_ON_READ                       0x1502
        // positioning error detected by reading
#define SCSIS_ASC_DATA_RECOV_NO_ERR_CORR        0x1700
        // recovered with no error correction applied
#define SCSIS_ASC_DATA_RECOV_WITH_RETRIES       0x1701
#define SCSIS_ASC_DATA_RECOV_POS_HEAD_OFS       0x1702
        // ?recovered with positive head offset
#define SCSIS_ASC_DATA_RECOV_NEG_HEAD_OFS       0x1703
        // ?recovered with negative head offset
#define SCSIS_ASC_DATA_RECOV_WITH_RETRIES_CIRC 0x1704
        // recovered with retries/CIRC
#define SCSIS_ASC_DATA_RECOV_PREV_SECT_ID       0x1705
        // recovered using previous sector ID
#define SCSIS_ASC_DATA_RECOV_NO_ECC_AUTOREALLOC 0x1706
#define SCSIS_ASC_DATA_RECOV_NO_ECC_REASSIGN 0x1707 // reassign recommended
#define SCSIS_ASC_DATA_RECOV_NO_ECC_REWRITE     0x1708  // rewrite recommended
#define SCSIS_ASC_DATA_RECOV_WITH_CORR          0x1800
        // recovered using error correction
#define SCSIS_ASC_DATA_RECOV_WITH_CORR_RETRIES 0x1801
        // used error correction and retries
#define SCSIS_ASC_DATA_RECOV_AUTOREALLOC        0x1802
#define SCSIS_ASC_DATA_RECOV_CIRC                       0x1803  // recovered using CIRC
#define SCSIS_ASC_DATA_RECOV_LEC                        0x1804  // recovered using LEC
#define SCSIS_ASC_DATA_RECOV_REASSIGN           0x1805  // reassign recommended
#define SCSIS_ASC_DATA_RECOV_REWRITE            0x1806  // rewrite recommended
#define SCSIS_ASC_PARAM_LIST_LENGTH_ERR         0x1a00  // parameter list too short
#define SCSIS_ASC_ID_RECOV                                      0x1e00  // recoved ID with ECC
#define SCSIS_ASC_INV_OPCODE                            0x2000
#define SCSIS_ASC_LBA_OOR                                       0x2100  // LBA out of range
#define SCSIS_ASC_ILL_FUNCTION                          0x2200
        // better use 0x2000/0x2400/0x2600 instead
#define SCSIS_ASC_INV_CDB_FIELD                         0x2400
#define SCSIS_ASC_LUN_NOT_SUPPORTED                     0x2500
#define SCSIS_ASC_INV_PARAM_LIST_FIELD          0x2600
#define SCSIS_ASC_PARAM_NOT_SUPPORTED           0x2601
#define SCSIS_ASC_PARAM_VALUE_INV                       0x2602
#define SCSIS_ASC_WRITE_PROTECTED                       0x2700
#define SCSIS_ASC_MEDIUM_CHANGED                        0x2800
#define SCSIS_ASC_WAS_RESET                                     0x2900
        // reset by power-on/bus reset/device reset
#define SCSIS_ASC_PARAMS_CHANGED                        0x2a00
#define SCSIS_ASC_CAPACITY_DATA_HAS_CHANGED     0x2a09
#define SCSIS_ASC_MEDIUM_FORMAT_CORRUPTED       0x3100
#define SCSIS_ASC_ROUNDED_PARAM                         0x3700  // parameter got rounded
#define SCSIS_ASC_NO_MEDIUM                                     0x3a00  // medium not present
#define SCSIS_ASC_INTERNAL_FAILURE                      0x4400
#define SCSIS_ASC_SEL_FAILURE                           0x4500  // select/reselect failure
#define SCSIS_ASC_UNSUCC_SOFT_RESET                     0x4600  // unsuccessful soft reset
#define SCSIS_ASC_SCSI_PARITY_ERR                       0x4700  // SCSI parity error
#define SCSIS_ASC_LOAD_EJECT_FAILED                     0x5300
        // media load or eject failed
#define SCSIS_ASC_REMOVAL_PREVENTED                     0x5302  // medium removal prevented
#define SCSIS_ASC_REMOVAL_REQUESTED                     0x5a01
        // operator requests medium removal

// some scsi op-codes
#define SCSI_OP_TEST_UNIT_READY                         0x00
#define SCSI_OP_REQUEST_SENSE                           0x03
#define SCSI_OP_FORMAT                                          0x04
#define SCSI_OP_READ_6                                          0x08
#define SCSI_OP_WRITE_6                                         0x0a
#define SCSI_OP_INQUIRY                                         0x12
#define SCSI_OP_VERIFY_6                                        0x13
#define SCSI_OP_MODE_SELECT_6                           0x15
#define SCSI_OP_RESERVE                                         0x16
#define SCSI_OP_RELEASE                                         0x17
#define SCSI_OP_MODE_SENSE_6                            0x1a
#define SCSI_OP_START_STOP                                      0x1b
#define SCSI_OP_RECEIVE_DIAGNOSTIC                      0x1c
#define SCSI_OP_SEND_DIAGNOSTIC                         0x1d
#define SCSI_OP_PREVENT_ALLOW                           0x1e
#define SCSI_OP_READ_CAPACITY                           0x25
#define SCSI_OP_READ_10                                         0x28
#define SCSI_OP_WRITE_10                                        0x2a
#define SCSI_OP_POSITION_TO_ELEMENT                     0x2b
#define SCSI_OP_VERIFY_10                                       0x2f
#define SCSI_OP_SYNCHRONIZE_CACHE                       0x35
#define SCSI_OP_WRITE_BUFFER                            0x3b
#define SCSI_OP_READ_BUFFER                                     0x3c
#define SCSI_OP_CHANGE_DEFINITION                       0x40
#define SCSI_OP_WRITE_SAME_10                           0x41
#define SCSI_OP_UNMAP                                           0x42
#define SCSI_OP_READ_SUB_CHANNEL                        0x42
#define SCSI_OP_READ_TOC                                        0x43
#define SCSI_OP_PLAY_MSF                                        0x47
#define SCSI_OP_PLAY_AUDIO_TRACK_INDEX          0x48    // obsolete, spec missing
#define SCSI_OP_PAUSE_RESUME                            0x4b
#define SCSI_OP_STOP_PLAY                                       0x4e
#define SCSI_OP_MODE_SELECT_10                          0x55
#define SCSI_OP_MODE_SENSE_10                           0x5a
#define SCSI_OP_VARIABLE_LENGTH_CDB                     0x7f
#define SCSI_OP_READ_16                                         0x88
#define SCSI_OP_WRITE_16                                        0x8a
#define SCSI_OP_VERIFY_16                                       0x8f
#define SCSI_OP_WRITE_SAME_16                           0x93
#define SCSI_OP_SERVICE_ACTION_IN                       0x9e
#define SCSI_OP_SERVICE_ACTION_OUT                      0x9f
#define SCSI_OP_MOVE_MEDIUM                                     0xa5
#define SCSI_OP_READ_12                                         0xa8
#define SCSI_OP_WRITE_12                                        0xaa
#define SCSI_OP_VERIFY_12                                       0xaf
#define SCSI_OP_READ_ELEMENT_STATUS                     0xb8
#define SCSI_OP_SCAN                                            0xba
#define SCSI_OP_READ_CD                                         0xbe

// Service-Action-In defines
#define SCSI_SAI_READ_CAPACITY_16                       0x10
#define SCSI_SAI_READ_LONG                                      0x11

// Service-Action-Out defines
#define SCSI_SAO_WRITE_LONG                                     0x11


// INQUIRY

typedef struct scsi_cmd_inquiry {
        uint8   opcode;
        B_LBITFIELD8_3(
                evpd : 1,                                               // enhanced vital product data
                _res1_1 : 4,
                lun : 3
        );
        uint8   page_code;
        uint8   _res3;
        uint8   allocation_length;
        uint8   control;
} _PACKED scsi_cmd_inquiry;

typedef struct scsi_res_inquiry {
        B_LBITFIELD8_2(
                device_type : 5,
                device_qualifier : 3
        );
        B_LBITFIELD8_2(
                device_type_modifier : 7,               // obsolete, normally set to zero
                removable_medium : 1
        );
        B_LBITFIELD8_3(                                         // 0 always means "not conforming"
                ansi_version : 3,                               // 1 for SCSI-1, 2 for SCSI-2 etc.
                ecma_version : 3,
                iso_version : 2
        );
        B_LBITFIELD8_4(
                response_data_format : 4,               // 2 = SCSI/2 compliant
                _res3_4 : 2,
                term_iop : 1,                                   // 1 = supports TERMINATE I/O PROCESS
                async_enc : 1                                   // processor devices only :
                                                                                // Asynchronous Event Notification Capable
        );
        uint8   additional_length;                      // total (whished) length = this + 4
        B_LBITFIELD8_2(
                protect : 1,
                _res5_1 : 7
        );
        uint8   _res6;
        B_LBITFIELD8_8(
                soft_reset : 1,                                 // 0 = soft reset leads to hard reset
                cmd_queue : 1,                                  // 1 = supports tagged command queuing
                _res7_2 : 1,
                linked : 1,                                             // 1 = supports linked commands
                sync : 1,                                               // 1 = supports synchronous transfers
                write_bus16 : 1,                                // 1 = supports 16 bit transfers
                write_bus32 : 1,                                // 1 = supports 32 bit transfers
                relative_address : 1                    // 1 = supports relative addr. for linking
        );
        char    vendor_ident[8];
        char    product_ident[16];
        char    product_rev[4];

        // XPT doesn't return following data on XPT_GDEV_TYPE
        uint8   vendor_spec[20];
        uint8   _res56[2];

        uint16  version_descriptor[8];          // array of supported standards, big endian

        uint8   _res74[22];
        /* additional vendor specific data */
} _PACKED scsi_res_inquiry;

enum scsi_peripheral_qualifier {
        scsi_periph_qual_connected = 0,
        scsi_periph_qual_not_connected = 2,
        scsi_periph_qual_not_connectable = 3
        // value 1 is reserved, values of 4 and above are vendor-specific
};

enum scsi_device_type {
        scsi_dev_direct_access = 0,
        scsi_dev_sequential_access = 1,
        scsi_dev_printer = 2,
        scsi_dev_processor = 3,
        scsi_dev_WORM = 4,
        scsi_dev_CDROM = 5,
        scsi_dev_scanner = 6,
        scsi_dev_optical = 7,
        scsi_dev_medium_changer = 8,
        scsi_dev_communication = 9,
        // 0xa - 0xb are graphics arts pre-press devices
        // 0xc - 0x1e reserved
        scsi_dev_storage_array = 0xc,
        scsi_dev_enclosure_services = 0xd,
        scsi_dev_simplified_direct_access = 0xe,
        scsi_dev_optical_card = 0xf,
        scsi_dev_unknown = 0x1f         // used for scsi_periph_qual_not_connectable
};


// vital product data: pages
#define SCSI_PAGE_SUPPORTED_VPD 0x00    /* Supported VPD Pages */
#define SCSI_PAGE_USN 0x80                              /* Unit serial number */
#define SCSI_PAGE_BLOCK_LIMITS 0xb0             /* Block limits */
#define SCSI_PAGE_BLOCK_DEVICE_CHARS 0xb1       /* Block device characteristics */
#define SCSI_PAGE_LB_PROVISIONING 0xb2  /* Logical block provisioning */
#define SCSI_PAGE_REFERRALS 0xb3                /* Referrals */

// vital product data: supported pages
typedef struct scsi_page_list {
        B_LBITFIELD8_2(
                device_type : 5,
                device_qualifier : 3
        );
        uint8   page_code;
        uint8   _res2;

        uint8   page_length;
        uint8   pages[1];                       // size according to page_length
} _PACKED scsi_page_list;

// vital product data: unit serial number page
typedef struct scsi_page_usn {
        B_LBITFIELD8_2(
                device_type : 5,
                device_qualifier : 3
        );
        uint8   page_code;
        uint8   _res2;

        uint8   _page_length;           // total size = this + 3
        char    psn[1];                 // size according to page_length
} _PACKED scsi_page_usn;

typedef struct scsi_page_block_limits {
        B_LBITFIELD8_2(
                device_type : 5,
                device_qualifier : 3
        );
        uint8   page_code;

        uint16  page_length;
        B_LBITFIELD8_2(
                wsnz : 1,
                _res4_1 : 7
        );
        uint8   max_cmp_write_length;
        uint16  opt_transfer_length_grain;
        uint32  max_transfer_length;
        uint32  opt_transfer_length;
        uint32  max_prefetch_length;
        uint32  max_unmap_lba_count;
        uint32  max_unmap_blk_count;
        uint32  opt_unmap_grain;
        uint32  unmap_grain_align;
        uint64  max_write_same_length;
        uint8   _res44[20];
} _PACKED scsi_page_block_limits;

typedef struct scsi_page_lb_provisioning {
        B_LBITFIELD8_2(
                device_type : 5,
                device_qualifier : 3
        );
        uint8   page_code;

        uint16  page_length;
        uint8   threshold_exponent;
        B_LBITFIELD8_7(
                dp : 1,
                anc_sup : 1,
                lbprz : 1,
                _res5_3 : 2,
                lbpws10 : 1,
                lbpws : 1,
                lbpu : 1
        );
        B_LBITFIELD8_2(
                provisioning_type : 3,
                _res6_3 : 5
        );
        uint8 _res7;
} _PACKED scsi_page_lb_provisioning;


// READ CAPACITY (10)

typedef struct scsi_cmd_read_capacity {
        uint8   opcode;
        B_LBITFIELD8_3(
                relative_address : 1,           // relative address
                _res1_1 : 4,
                lun : 3
        );
        uint32  lba;
        uint8   _res6[2];
        B_LBITFIELD8_2(
                pmi : 1,                                                        // partial medium indicator
                _res8_1 : 7
        );
        uint8   control;
} _PACKED scsi_cmd_read_capacity;

typedef struct scsi_res_read_capacity {
        uint32  lba;                                    // big endian
        uint32  block_size;                             // in bytes
} _PACKED scsi_res_read_capacity;

// READ CAPACITY (16)

typedef struct scsi_cmd_read_capacity_long {
        uint8   opcode;
        uint8   service_action;
        uint64  lba;
        uint32  alloc_length;
        B_LBITFIELD8_2(
                pmi : 1,
                _res14_1 : 7
        );
        uint8   control;
} _PACKED scsi_cmd_read_capacity_long;

typedef struct scsi_res_read_capacity_long {
        uint64  lba;                                    // big endian
        uint32  block_size;                             // in bytes
        B_LBITFIELD8_4(
                prot_en : 1,
                p_type : 3,
                rc_basis : 2,
                _res12_6 : 2
        );
        B_LBITFIELD8_2(
                logical_blocks_per_physical_block_exponent : 4,
                p_i_exponent : 4
        );
        B_LBITFIELD8_3(
                lowest_aligned_lba_p1 : 6,
                        // first part of the Lowest Aligned LBA field
                lbprz : 1,
                lbpme : 1
        );
        uint8   lowest_aligned_lba_p2;
                // second part of the Lowest Aligned LBA field
                // (B_LBITFIELD16_3 would not help here because of its alignment)
        uint8   _res16[16];
} _PACKED scsi_res_read_capacity_long;


// READ (6), WRITE (6)

typedef struct scsi_cmd_rw_6 {
        uint8   opcode;
        B_LBITFIELD8_2(
                high_lba : 5,
                lun : 3
        );
        uint8   mid_lba;
        uint8   low_lba;
        uint8   length;                                 // 0 = 256 blocks
        uint8   control;
} _PACKED scsi_cmd_rw_6;


// READ (10), WRITE (10)

typedef struct scsi_cmd_rw_10 {
        uint8   opcode;
        B_LBITFIELD8_5(
                relative_address : 1,           // relative address
                _res1_1 : 2,
                force_unit_access : 1,          // force unit access (1 = safe, cacheless access)
                disable_page_out : 1,           // disable page out (1 = not worth caching)
                lun : 3
        );
        uint32  lba;                                    // big endian
        uint8   _res6;
        uint16  length;                                 // 0 = no block
        uint8   control;
} _PACKED scsi_cmd_rw_10;


// READ (12), WRITE (12)

typedef struct scsi_cmd_rw_12 {
        uint8   opcode;
        B_LBITFIELD8_5(
                relative_address : 1,           // relative address
                _res1_1 : 2,
                force_unit_access : 1,          // force unit access (1 = safe, cacheless access)
                disable_page_out : 1,           // disable page out (1 = not worth caching)
                lun : 3
        );
        uint32  lba;                                    // big endian
        uint32  length;                                 // 0 = no block
        uint8   _res10;
        uint8   control;
} _PACKED scsi_cmd_rw_12;


// READ (16), WRITE (16)

typedef struct scsi_cmd_rw_16 {
        uint8   opcode;
        B_LBITFIELD8_6(
                _res1_0 : 1,
                force_unit_access_non_volatile : 1,
                _res1_2 : 1,
                force_unit_access : 1,
                disable_page_out : 1,
                read_protect : 3
        );
        uint64  lba;                                    // big endian
        uint32  length;
        B_LBITFIELD8_3(
                group_number : 5,
                _res_14_5 : 2,
                _res_14_7 : 1
        );
        uint8   control;
} _PACKED scsi_cmd_rw_16;


// WRITE SAME (10)

typedef struct scsi_cmd_wsame_10 {
        uint8   opcode;
        B_LBITFIELD8_6(
                _obsolete1_0 : 1,
                _obsolete1_1 : 1,
                _obsolete1_2 : 1,
                unmap : 1,
                anchor : 1,
                write_protect : 3
        );
        uint32  lba;
        B_LBITFIELD8_2(
                group_number : 5,
                _res6_5 : 3
        );
        uint16  length;
        uint8   control;
} _PACKED scsi_cmd_wsame_10;


// WRITE SAME (16)

typedef struct scsi_cmd_wsame_16 {
        uint8   opcode;
        B_LBITFIELD8_6(
                ndob : 1,
                lb_data : 1,
                pb_data : 1,
                unmap : 1,
                anchor : 1,
                write_protect : 3
        );
        uint64  lba;
        uint32  length;
        B_LBITFIELD8_2(
                group_number : 5,
                _res14_5 : 3
        );
        uint8   control;
} _PACKED scsi_cmd_wsame_16;


// UNMAP

typedef struct scsi_cmd_unmap {
        uint8   opcode;
        B_LBITFIELD8_2(
                anchor : 1,
                _reserved1_7 : 7
        );
        uint32  _reserved1;
        B_LBITFIELD8_2(
                group_number : 5,
                _reserved5_7 : 3
        );
        uint16  length;
        uint8   control;
} _PACKED scsi_cmd_unmap;

struct scsi_unmap_block_descriptor {
        uint64  lba;
        uint32  block_count;
        uint32  _reserved1;
} _PACKED;

struct scsi_unmap_parameter_list {
        uint16  data_length;
        uint16  block_data_length;
        uint32  _reserved1;
        struct scsi_unmap_block_descriptor blocks[1];
} _PACKED;


// REQUEST SENSE

typedef struct scsi_cmd_request_sense {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res2[2];
        uint8   allocation_length;
        uint8   control;
} _PACKED scsi_cmd_request_sense;


// sense data structures

#define SCSIS_CURR_ERROR 0x70
#define SCSIS_DEFERRED_ERROR 0x71

typedef struct scsi_sense {
        B_LBITFIELD8_2(
                error_code : 7,
                valid : 1                                                       // 0 = not conforming to standard
        );
        uint8 segment_number;                                   // for COPY/COPY AND VERIFY/COMPARE
        B_LBITFIELD8_5(
                sense_key : 4,
                res2_4 : 1,
                ILI : 1,                                                        // incorrect length indicator - req. block
                                                                                        // length doesn't match physical block length
                EOM : 1,                                                        // serial devices only
                Filemark : 1                                            // optional for random access
        );

        uint8 highest_inf;                                              // device-type or command specific
        uint8 high_inf;                                                 // device-type 0, 4, 5, 7: block address
        uint8 mid_inf;                                                  // device-type 1, 2, 3: req length - act. length
        uint8 low_inf;                                                  // (and others for sequential dev. and COPY cmds

        uint8 add_sense_length;                                 // total length = this + 7

        uint8 highest_cmd_inf;
        uint8 high_cmd_inf;
        uint8 mid_cmd_inf;
        uint8 low_cmd_inf;
        uint8 asc;
        uint8 ascq;                                                             // this can be zero if unsupported
        uint8 unit_code;                                                // != 0 to specify internal device unit

        union {
                struct {
                B_LBITFIELD8_2(
                        high_key_spec : 7,
                        SKSV : 1                                                // 1 = sense key specific (byte 15-17) valid
                );
                uint8 mid_key_spec;
                uint8 low_key_spec;
                } raw;

                // ILLEGAL REQUEST
                struct {
                B_LBITFIELD8_5(
                        bit_pointer : 3,                                // points to (highest) invalid bit of parameter
                        BPV : 1,                                                // 1 = bit_pointer is valid
                        res15_4 : 2,
                        c_d : 2,                                                // 1 = error command, 0 = error in data
                        SKSV : 1                                                // s.a.
                );
                uint8 high_field_pointer;                       // points to (highest) invalid byte of parameter
                uint8 low_field_pointer;                        // (!using big endian, this means the first byte!)
                } ill_request;

                // access error (RECOVERED, HARDWARE or MEDIUM ERROR)
                struct {
                B_LBITFIELD8_2(
                        res15_0 : 7,
                        SKSV : 1
                );
                uint8 high_retry_cnt;
                uint8 low_retry_cnt;
                } acc_error;

                // format progress (if sense key = NOT READY)
                struct {
                B_LBITFIELD8_2(
                        res15_0 : 7,
                        SKSV : 1
                );
                uint16  progress;                               // 0 = start, 0xffff = almost finished
                } format_progress;
        } sense_key_spec;

        // starting with offset 18 there are additional sense byte
} _PACKED scsi_sense;


// PREVENT ALLOW

typedef struct scsi_cmd_prevent_allow {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res2[2];
        B_LBITFIELD8_2(
                prevent : 1,            // 1 - prevent medium removal, 0 - allow removal
                _res4_1 : 7
        );
        uint8   control;
} _PACKED scsi_cmd_prevent_allow;

// START STOP UNIT

typedef struct scsi_cmd_ssu {
        uint8   opcode;
        B_LBITFIELD8_3(
                immediately : 1,                        // 1 - return immediately, 0 - return on completion
                _res1_1 : 4,
                lun : 3
        );
        uint8 res2[2];
        B_LBITFIELD8_3(
                start : 1,                      // 1 - load+start, i.e. allow, 0 - eject+stop, i.e. deny
                load_eject : 1,                 // 1 - include loading/ejecting, 0 - only to allow/deny
                _res4_2 : 6
        );
        uint8   control;
} _PACKED scsi_cmd_ssu;


// MODE SELECT (6)

typedef struct scsi_cmd_mode_select_6 {
        uint8   opcode;
        B_LBITFIELD8_4(
                save_pages : 1,         // 1 = save pages to non-volatile memory
                _res1_1 : 3,
                pf : 1,                         // 0 = old SCSI-1; 1 = new SCSI-2 format
                lun : 3
        );
        uint8   _res2[2];
        uint8   param_list_length;      // data size
        uint8   control;
} _PACKED scsi_cmd_mode_select_6;


// MODE SENSE (6)

typedef struct scsi_cmd_mode_sense_6 {
        uint8   opcode;
        B_LBITFIELD8_4(
                _res1_0 : 3,
                disable_block_desc : 1,         // disable block descriptors
                _res1_4 : 1,
                lun : 3
        );
        B_LBITFIELD8_2(
                page_code : 6,
                page_control : 2                        // page control field
        );
        uint8   _res3;
        uint8   allocation_length;              // maximum amount of data
        uint8   control;
} _PACKED scsi_cmd_mode_sense_6;


// MODE SELECT (10)

typedef struct scsi_cmd_mode_select_10 {
        uint8   opcode;
        B_LBITFIELD8_4(
                save_pages : 1,                         // 1 = save pages to non-volatile memory
                _res1_1 : 3,
                pf : 1,                                         // 0 = old SCSI-1; 1 = new SCSI-2 format
                lun : 3
        );
        uint8   _res2[5];
        uint16  param_list_length;              // data size, big endian
        uint8   control;
} _PACKED scsi_cmd_mode_select_10;


// MODE SENSE (10)

typedef struct scsi_cmd_mode_sense_10 {
        uint8   opcode;
        B_LBITFIELD8_4(
                _res1_0 : 3,
                disable_block_desc : 1,         // disable block descriptors
                _res1_4 : 1,
                lun : 3
        );
        B_LBITFIELD8_2(
                page_code : 6,
                page_control : 2                        // page control field
        );
        uint8   _res3[4];
        uint16  allocation_length;              // maximum amount of data, big endian
        uint8   control;
} _PACKED scsi_cmd_mode_sense_10;

// possible contents of page control (PC)
#define SCSI_MODE_SENSE_PC_CURRENT 0
#define SCSI_MODE_SENSE_PC_CHANGABLE 1
        // changable field are filled with "1"
#define SCSI_MODE_SENSE_PC_DEFAULT 2
#define SCSI_MODE_SENSE_PC_SAVED 3

// special mode page indicating to return all mode pages
#define SCSI_MODEPAGE_ALL 0x3f

// header of mode data; followed by block descriptors and mode pages
typedef struct scsi_mode_param_header_6 {
        uint8   mode_data_length;               // total length excluding this byte
        uint8   medium_type;
        uint8   dev_spec_parameter;
        uint8   block_desc_length;              // total length of all transmitted block descriptors
} _PACKED scsi_mode_param_header_6;

typedef struct scsi_mode_param_header_10 {
        uint16  mode_data_length;               // total length excluding these two bytes
        uint8   medium_type;
        uint8   dev_spec_parameter;
        uint8   _res4[2];
        uint16  block_desc_length;              // total length of all transmitted block descriptors
} _PACKED scsi_mode_param_header_10;


// content of dev_spec_parameter for direct access devices
typedef struct scsi_mode_param_dev_spec_da {
        B_LBITFIELD8_4(
                _res0_0 : 4,
                dpo_fua : 1,                    // 1 = supports DPO and FUA, see READ (10) (sense only)
                _res0_6 : 1,
                write_protected : 1             // write protected (sense only)
        );
} _PACKED scsi_mode_param_dev_spec_da;

typedef struct scsi_mode_param_block_desc {
        uint8   density;                        // density code of area
        uint8   high_numblocks;         // size of this area in blocks
        uint8   med_numblocks;          // 0 = all remaining blocks
        uint8   low_numblocks;
        uint8   _res4;
        uint8   high_blocklen;          // block size
        uint8   med_blocklen;
        uint8   low_blocklen;
} _PACKED scsi_mode_param_block_desc;


// header of a mode pages
typedef struct scsi_modepage_header {
        B_LBITFIELD8_3(
                page_code : 6,
                _res0_6 : 1,
                PS : 1                          // 1 = page can be saved (only valid for MODE SENSE)
        );
        uint8   page_length;    // size of page excluding this common header
} _PACKED scsi_modepage_header;


// control mode page
#define SCSI_MODEPAGE_CONTROL 0xa

typedef struct scsi_modepage_control {
        scsi_modepage_header header;
        B_LBITFIELD8_2(
                RLEC : 1,                       // Report Log Exception Condition
                res2_1 : 7
        );
        B_LBITFIELD8_4(
                DQue : 1,                       // disable Queuing
                QErr : 1,                       // abort queued commands on contingent allegiance condition
                res3_2 : 2,
                QAM : 4                         // Queue Algorithm Modifier
        );
        B_LBITFIELD8_5(
                EAENP : 1,                      // error AEN permission; true = send AEN on deferred error
                                                        // false = generate UA condition after deferred error
                UAAENP : 1,                     // unit attention AEN permission; true = send AEN,
                                                        // false = generate UA condition (for everything but init.)
                RAENP : 1,                      // ready AEN permission; true = send async event notification
                                                        // (AEN) instead of generating an Unit Attention (UA) Condition
                                                        // after initialization
                res4_3 : 4,
                EECA : 1                        // enable Extended Contingent Allegiance
        );
        uint8 res5;
        uint8 high_AEN_holdoff; // ready AEN hold off period - delay in ms between
        uint8 low_AEN_holdoff;  // initialization and AEN
} scsi_modepage_control;

// values for QAM
#define SCSI_QAM_RESTRICTED 0
#define SCSI_QAM_UNRESTRICTED 1
// 2 - 7 reserved, 8 - 0xf vendor-specific


// CD audio control page
#define SCSI_MODEPAGE_AUDIO 0xe

typedef struct scsi_modepage_audio {
        scsi_modepage_header header;
        B_LBITFIELD8_4(
                _res2_0 : 1,
                stop_on_track_crossing : 1,             // Stop On Track Crossing
                        // 0 - stop according transfer length, 1 - stop at end of track
                immediately : 1,                                        // must be one
                _res2_3 : 5
        );
        uint8 _res3[3];
        uint8 _obsolete6[2];
        struct {
                B_LBITFIELD8_2(
                        channel : 4,    // select channel to connect to this port
                        _res0_4 : 4
                );
                uint8 volume;
        } ports[4];
} _PACKED scsi_modepage_audio;

// connection between output port and audio channel
#define SCSI_CHANNEL_SEL_MUTED          0       // mute port
#define SCSI_CHANNEL_SEL_CHANNEL0       1       // connect to channel 0
#define SCSI_CHANNEL_SEL_CHANNEL1       2       // connect to channel 1
#define SCSI_CHANNEL_SEL_CHANNEL0_1     3       // connect to channel 0 and channel 1
#define SCSI_CHANNEL_SEL_CHANNEL2       4       // connect to channel 2
#define SCSI_CHANNEL_SEL_CHANNEL3       8       // connect to channel 3

// TUR

typedef struct scsi_cmd_tur {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res3[3];
        uint8   control;
} _PACKED scsi_cmd_tur;


// READ_TOC

typedef struct scsi_cmd_read_toc {
        uint8   opcode;
        B_LBITFIELD8_4(
                _res1_0 : 1,
                time : 1,                                       // true, to use MSF format, false for LBA format
                _res1_2 : 3,
                lun : 3
        );
        B_LBITFIELD8_2(
                format : 4,                                     // see below
                _res2_4 : 4
        );
        uint8   _res3[3];
        uint8   track;                                  // (starting) track
        uint16  allocation_length;              // maximum amount of data (big endian)
        uint8   control;
} _PACKED scsi_cmd_read_toc;

// values of <format> in TOC command
#define SCSI_TOC_FORMAT_TOC 0                   // all TOCs starting with <track> (0xaa for lead-out)
#define SCSI_TOC_FORMAT_SESSION_INFO 1  // Session info
#define SCSI_TOC_FORMAT_FULL_TOC 2              // all Q-channel data in TOC
#define SCSI_TOC_FORMAT_PMA 3                   // Q-channel data in PMA area
#define SCSI_TOC_FORMAT_ATIP 4                  // get ATIP data
#define SCSI_TOC_FORMAT_CD_TEXT 5               // get CD-Text from R/W-channel in lead-in

// general structure of response
typedef struct scsi_toc_general {
        uint16  data_length;                            // big endian, total length - 2
        uint8   first;                                          // first track/session/reserved
        uint8   last;                                                   // last one
        // remainder are parameter list descriptors
} _PACKED scsi_toc_general;

// definition of CD-ROM LBA
typedef uint32 scsi_cd_lba;                             // big endian

// definition of CD-ROM MSF time
typedef struct scsi_cd_msf {
        uint8   _reserved;
        uint8   minute;
        uint8   second;
        uint8   frame;
} _PACKED scsi_cd_msf;

// definition of Track Number address format
typedef struct scsi_cd_track_number {
        uint8   _res0[3];
        uint8   track;
} _PACKED scsi_cd_track_number;

// one track for SCSI_TOC_FORMAT_TOC
typedef struct scsi_toc_track {
        uint8   _res0;
        B_LBITFIELD8_2(
                control : 4,
                adr : 4
        );
        uint8   track_number;           // track number (hex)
        uint8   _res3;
        union {                                 // start of track (time or LBA, see TIME of command)
                scsi_cd_lba lba;
                scsi_cd_msf time;
        } start;
} _PACKED scsi_toc_track;

// possible value of ADR-field (described Q-channel content)
enum scsi_adr {
        scsi_adr_none = 0,                              // no Q-channel mode info
        scsi_adr_position = 1,                  // Q-channel encodes current position data
        scsi_adr_mcn = 2,                               // Q-channel encodes Media Catalog Number
        scsi_adr_isrc = 3                               // Q-channel encodes ISRC
};

// value of Q-channel control field (CONTROL)
enum scsi_q_control {
        scsi_q_control_2audio                   = 0,    // stereo audio
        scsi_q_control_2audio_preemp    = 1,    // stereo audio with 50/15µs pre-emphasis
        scsi_q_control_1audio                   = 8,    // audio (reserved in CD-R/W)
        scsi_q_control_1audio_preemp    = 9,    // audio with pre-emphasis (reserved in CD-R/W)
        scsi_q_control_data_un_intr     = 4,    // data, recorded un-interrupted
        scsi_q_control_data_incr                = 5,    // data, recorded incremental
        scsi_q_control_ddcd                             = 4,    // DDCD data
        scsi_q_control_copy_perm                = 2             // copy permitted (or-ed with value above)
};

// format SCSI_TOC_FORMAT_TOC
typedef struct scsi_toc_toc {
        uint16  data_length;                    // big endian, total length - 2
        uint8   first_track;                    // first track
        uint8   last_track;                             // last track

        scsi_toc_track tracks[1];               // one entry per track
} _PACKED scsi_toc_toc;


// READ SUB-CHANNEL

typedef struct scsi_cmd_read_subchannel {
        uint8   opcode;
        B_LBITFIELD8_4(
                _res1_0 : 1,
                time : 1,                                       // true, to use MSF format, false for LBA format
                _res1_2 : 3,
                lun : 3
        );
        B_LBITFIELD8_3(
                _res2_0 : 6,
                subq : 1,                                       // 1 - return Q sub-channel data
                _res2_7 : 1
        );
        uint8   parameter_list;                 // see below
        uint8   _res4[2];
        uint8   track;                                  // track number (hex)
        uint16  allocation_length;              // maximum amount of data, big endian
        uint8   control;
} _PACKED scsi_cmd_read_subchannel;

// values of parameter_list
enum scsi_sub_channel_parameter_list {
        scsi_sub_channel_parameter_list_cd_pos  = 1,    // CD current position
        scsi_sub_channel_parameter_list_mcn             = 2,    // Media Catalog Number
        scsi_sub_channel_parameter_list_isrc    = 3             // Track International Standard Recording Code
};

// header of response
typedef struct scsi_subchannel_data_header {
        uint8   _res0;
        uint8   audio_status;                   // see below
        uint16  data_length;                    // total length - 4, big endian
} _PACKED scsi_subchannel_data_header;

// possible audio_status
enum scsi_audio_status {
        scsi_audio_status_not_supported         = 0,
        scsi_audio_status_playing                       = 0x11,
        scsi_audio_status_paused                        = 0x12,
        scsi_audio_status_completed                     = 0x13,
        scsi_audio_status_error_stop            = 0x14,
        scsi_audio_status_no_status                     = 0x15
};

typedef struct scsi_cd_current_position {
        uint8   format_code;                    // always 1
        B_LBITFIELD8_2(
                control : 4,                            // see scsi_q_control
                adr : 4                                         // see scsi_adr
        );
        uint8   track;
        uint8   index;
        union {                                                 // current position, relative to logical beginning
                scsi_cd_lba lba;
                scsi_cd_msf time;
        } absolute_address;
        union {                                                 // current position, relative to track
                scsi_cd_lba lba;
                scsi_cd_msf time;
        } track_relative_address;
} _PACKED scsi_cd_current_position;


// PLAY AUDIO MSF

typedef struct scsi_cmd_play_msf {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res2;
        uint8   start_minute;                   // start time
        uint8   start_second;
        uint8   start_frame;
        uint8   end_minute;                             // end time (excluding)
        uint8   end_second;
        uint8   end_frame;
        uint8   control;
} _PACKED scsi_cmd_play_msf;


// STOP AUDIO

typedef struct scsi_cmd_stop_play {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res2[7];
        uint8   control;
} _PACKED scsi_cmd_stop_play;


// PAUSE/RESUME

typedef struct scsi_cmd_pause_resume {
        uint8   opcode;
        B_LBITFIELD8_2(
                _res1_0 : 5,
                lun : 3
        );
        uint8   _res2[6];
        B_LBITFIELD8_2(
                resume : 1,                             // 1 for resume, 0 for pause
                _res8_2 : 7
        );
        uint8   control;
} _PACKED scsi_cmd_pause_resume;


// SCAN

typedef struct scsi_cmd_scan {
        uint8   opcode;
        B_LBITFIELD8_4(
                relative_address : 1,   // must be zero
                _res1_1 : 3,
                direct : 1,                             // direction: 0 forward, 1 backward
                lun : 3
        );
        union {                                         // start of track (depends on <type>)
                scsi_cd_lba lba;
                scsi_cd_msf time;
                scsi_cd_track_number track_number;
        } start;
        uint8   _res6[3];
        B_LBITFIELD8_2(
                res9_0 : 6,
                type : 2                                // actual type of <start> (see below)
        );
        uint8   _res10;
        uint8   control;
} _PACKED scsi_cmd_scan;

// possible values for type
enum scsi_scan_type {
        scsi_scan_lba = 0,
        scsi_scan_msf = 1,
        scsi_scan_tno = 2
};


// READ_CD

typedef struct scsi_cmd_read_cd {
        uint8   opcode;
        B_LBITFIELD8_4(
                relative_address : 1,   // must be zero
                _res1_1 : 1,
                sector_type : 3,                // required sector type (1=CDDA)
                lun : 3
        );
        scsi_cd_lba lba;
        uint8   high_length;
        uint8   mid_length;
        uint8   low_length;
        B_LBITFIELD8_6(
                _res9_0 : 1,
                error_field : 2,
                edc_ecc : 1,                    // include EDC/ECC; includes 8 byte padding for Mode 1 format
                user_data : 1,                  // if 1, include user data
                                                                // (mode select block size is ignored)
                header_code : 2,
                sync : 1                                // if 1, include sync field from sector
        );
        B_LBITFIELD8_2(
                sub_channel_selection : 4,
                _res10_4 : 4
        );
        uint8   control;
} _PACKED scsi_cmd_read_cd;

// possible values for header_code
enum scsi_read_cd_header_code {
        scsi_read_cd_header_none                        = 0,
        scsi_read_cd_header_hdr_only            = 1,
        scsi_read_cd_header_sub_header_only     = 2,
        scsi_read_cd_header_all_headers         = 3,
};

// possible values for error_field
enum scsi_read_cd_error_field {
        scsi_read_cd_error_none                                 = 0,
        scsi_read_cd_error_c2_error                             = 1, // include 2352 bits indicating error in byte
        scsi_read_cd_error_c2_and_block_error   = 2, // include or of C2 data plus pad byte
};

// possible values for sub_channel_selection
enum scsi_read_cd_sub_channel_selection {
        scsi_read_cd_sub_channel_none                   = 0,
        scsi_read_cd_sub_channel_RAW                    = 1,
        scsi_read_cd_sub_channel_Q                              = 2,
        scsi_read_cd_sub_channel_P_W                    = 4     // R/W data, depending on CD capabilities
                                                                                                // and Mechanism status page
};

// SYNCHRONIZE CACHE (10)

typedef struct scsi_cmd_sync_cache {
        uint8   opcode;
        B_LBITFIELD8_4(
                relative_address : 1,   // must be zero
                immediately : 1,                // 1 - return immediately, 0 - return on completion
                _res1_1 : 3,
                lun : 3
        );
        scsi_cd_lba lba;
        uint8   _res2;
        uint16  block_count;            // big endian
        uint8   control;
} _PACKED scsi_cmd_sync_cache;


#endif  /* _SCSI_CMDS_H */