/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Definitions for the generic SCSI device command handler.
 *
 * Authors: Miran Grca, <mgrca8@gmail.com>
 *          Fred N. van Kempen, <decwiz@yahoo.com>
 *
 *          Copyright 2016-2019 Miran Grca.
 *          Copyright 2017-2019 Fred N. van Kempen.
 */
#ifndef SCSI_DEVICE_H
#define SCSI_DEVICE_H

/* Configuration. */
#define SCSI_NUM         (SCSI_BUS_MAX * SCSI_ID_MAX)

#define SCSI_LUN_USE_CDB 0xff

#ifdef WALTJE
#    define SCSI_TIME 50.0
#else
#    define SCSI_TIME 500.0
#endif

/* Bits of 'status' */
#define ERR_STAT     0x01
#define DRQ_STAT     0x08 /* Data request */
#define DSC_STAT     0x10
#define SERVICE_STAT 0x10
#define READY_STAT   0x40
#define BUSY_STAT    0x80

/* Bits of 'error' */
#define ABRT_ERR 0x04 /* Command aborted */
#define MCR_ERR  0x08 /* Media change request */

/* SCSI commands. */
#define GPCMD_TEST_UNIT_READY                         0x00
#define GPCMD_REZERO_UNIT                             0x01
#define GPCMD_REWIND                                  0x01
#define GPCMD_REQUEST_BLOCK_ADDRESS                   0x02
#define GPCMD_REQUEST_SENSE                           0x03
#define GPCMD_FORMAT_UNIT                             0x04
#define GPCMD_READ_BLOCK_LIMITS                       0x05
#define GPCMD_IOMEGA_SENSE                            0x06
#define GPCMD_READ_6                                  0x08
#define GPCMD_WRITE_6                                 0x0a
#define GPCMD_SEEK_6                                  0x0b
#define GPCMD_IOMEGA_SET_PROTECTION_MODE              0x0c
#define GPCMD_SEEK_BLOCK                              0x0c
#define GPCMD_WRITE_FILEMARKS_6                       0x10
#define GPCMD_SPACE_6                                 0x11
#define GPCMD_NO_OPERATION_TOSHIBA                    0x0d /* Toshiba Vendor Unique command. */
#define GPCMD_NO_OPERATION_NEC                        0x0d /* NEC Vendor Unique command. */
#define GPCMD_IOMEGA_EJECT                            0x0d /* ATAPI only? */
#define GPCMD_PARTITION                               0x0d
#define GPCMD_INQUIRY                                 0x12
#define GPCMD_VERIFY_6                                0x13
#define GPCMD_MODE_SELECT_6                           0x15
#define GPCMD_SCSI_RESERVE                            0x16
#define GPCMD_SCSI_RELEASE                            0x17
#define GPCMD_MODE_SENSE_6                            0x1a
#define GPCMD_START_STOP_UNIT                         0x1b
#define GPCMD_ERASE_6                                 0x19
#define GPCMD_SEND_DIAGNOSTIC                         0x1d
#define GPCMD_PREVENT_REMOVAL                         0x1e
#define GPCMD_READ_FORMAT_CAPACITIES                  0x23
#define GPCMD_READ_CDROM_CAPACITY                     0x25
#define GPCMD_UNKNOWN_CHINON                          0x26 /* Chinon Vendor Unique command. */
#define GPCMD_READ_10                                 0x28
#define GPCMD_READ_GENERATION                         0x29
#define GPCMD_WRITE_10                                0x2a
#define GPCMD_SEEK_10                                 0x2b
#define GPCMD_LOCATE_10                               0x2b
#define GPCMD_ERASE_10                                0x2c
#define GPCMD_WRITE_AND_VERIFY_10                     0x2e
#define GPCMD_VERIFY_10                               0x2f
#define GPCMD_READ_POSITION                           0x34
#define GPCMD_SYNCHRONIZE_CACHE                       0x35
#define GPCMD_READ_BUFFER                             0x3c
#define GPCMD_WRITE_SAME_10                           0x41
#define GPCMD_READ_SUBCHANNEL                         0x42
#define GPCMD_READ_TOC_PMA_ATIP                       0x43
#define GPCMD_READ_HEADER                             0x44
#define GPCMD_PLAY_AUDIO_10                           0x45
#define GPCMD_GET_CONFIGURATION                       0x46
#define GPCMD_PLAY_AUDIO_MSF                          0x47
#define GPCMD_PLAY_AUDIO_TRACK_INDEX                  0x48
#define GPCMD_PLAY_AUDIO_TRACK_RELATIVE_10            0x49
#define GPCMD_GET_EVENT_STATUS_NOTIFICATION           0x4a
#define GPCMD_PAUSE_RESUME                            0x4b
#define GPCMD_STOP_PLAY_SCAN                          0x4e
#define GPCMD_READ_DISC_INFORMATION                   0x51
#define GPCMD_READ_TRACK_INFORMATION                  0x52
#define GPCMD_MODE_SELECT_10                          0x55
#define GPCMD_MODE_SENSE_10                           0x5a
#define GPCMD_CLOSE_TRACK_SESSION                     0x5b
#define GPCMD_READ_BUFFER_CAPACITY                    0x5c
#define GPCMD_SEND_CUE_SHEET                          0x5d
#define GPCMD_PLAY_AUDIO_12                           0xa5
#define GPCMD_READ_12                                 0xa8
#define GPCMD_PLAY_AUDIO_TRACK_RELATIVE_12            0xa9
#define GPCMD_WRITE_12                                0xaa
#define GPCMD_ERASE_12                                0xac
#define GPCMD_READ_DVD_STRUCTURE                      0xad /* For reading. */
#define GPCMD_WRITE_AND_VERIFY_12                     0xae
#define GPCMD_VERIFY_12                               0xaf
#define GPCMD_PLAY_CD_OLD                             0xb4
#define GPCMD_READ_CD_OLD                             0xb8 /* Should be equivalent to 0xbe */
#define GPCMD_READ_CD_MSF                             0xb9
#define GPCMD_AUDIO_SCAN                              0xba
#define GPCMD_SET_SPEED                               0xbb
#define GPCMD_PLAY_CD                                 0xbc /* At some point, this was READ CD, according to the
                                                              ATAPI specification */
#define GPCMD_MECHANISM_STATUS                        0xbd
#define GPCMD_READ_CD                                 0xbe
#define GPCMD_SEND_DVD_STRUCTURE                      0xbf /* This is for writing only, irrelevant to 86Box. */
#define GPCMD_AUDIO_TRACK_SEARCH_TOSHIBA              0xc0 /* Toshiba Vendor Unique command */
#define GPCMD_EJECT_CHINON                            0xc0 /* Chinon Vendor Unique command */
#define GPCMD_MAGAZINE_EJECT_PIONEER                  0xc0 /* Pioneer Vendor Unique command */
#define GPCMD_SET_ADDRESS_FORMAT_SONY                 0xc0 /* Sony Vendor Unique command */
#define GPCMD_PLAY_AUDIO_TOSHIBA                      0xc1 /* Toshiba Vendor Unique command */
#define GPCMD_READ_TOC_PIONEER                        0xc1 /* Pioneer Vendor Unique command */
#define GPCMD_READ_TOC_SONY                           0xc1 /* Sony Vendor Unique command */
#define GPCMD_PAUSE_RESUME_ALT                        0xc2
#define GPCMD_READ_SUBCHANNEL_MATSUSHITA              0xc2 /* Matsushita Vendor Unique command */
#define GPCMD_READ_SUBCODEQ_PIONEER                   0xc2 /* Pioneer Vendor Unique command */
#define GPCMD_READ_SUBCHANNEL_SONY                    0xc2 /* Sony Vendor Unique command */
#define GPCMD_STILL_TOSHIBA                           0xc2 /* Toshiba Vendor Unique command */
#define GPCMD_READ_TOC_MATSUSHITA                     0xc3 /* Matsushita Vendor Unique command */
#define GPCMD_READ_HEADER_SONY                        0xc3 /* Sony Vendor Unique command */
#define GPCMD_SET_STOP_TIME_TOSHIBA                   0xc3 /* Toshiba Vendor Unique command */
#define GPCMD_CADDY_EJECT_TOSHIBA                     0xc4 /* Toshiba Vendor Unique command */
#define GPCMD_PLAYBACK_STATUS_SONY                    0xc4 /* Sony Vendor Unique command */
#define GPCMD_READ_HEADER_MATSUSHITA                  0xc4 /* Matsushita Vendor Unique command */
#define GPCMD_PAUSE_SONY                              0xc5 /* Sony Vendor Unique command */
#define GPCMD_PLAY_AUDIO_MATSUSHITA                   0xc5 /* Matsushita Vendor Unique command */
#define GPCMD_UNKNOWN_SCSI2_NEC                       0xc5 /* NEC Vendor Unique Command */
#define GPCMD_PLAY_TRACK_SONY                         0xc6 /* Sony Vendor Unique command */
#define GPCMD_READ_SUBCODEQ_PLAYING_STATUS_TOSHIBA    0xc6 /* Toshiba Vendor Unique command */
#define GPCMD_STOP_CHINON                             0xc6 /* Chinon Vendor Unique command */
#define GPCMD_PLAY_AUDIO_MSF_MATSUSHITA               0xc7 /* Matsushita Vendor Unique command*/
#define GPCMD_PLAY_MSF_SONY                           0xc7 /* Sony Vendor Unique command*/
#define GPCMD_READ_DISC_INFORMATION_TOSHIBA           0xc7 /* Toshiba Vendor Unique command */
#define GPCMD_AUDIO_TRACK_SEARCH_PIONEER              0xc8 /* Pioneer Vendor Unique command */
#define GPCMD_PLAY_AUDIO_SONY                         0xc8 /* Sony Vendor Unique command */
#define GPCMD_PLAY_AUDIO_TRACK_INDEX_MATSUSHITA       0xc8 /* Matsushita Vendor Unique command */
#define GPCMD_READ_CDROM_MODE_TOSHIBA                 0xc8 /* Toshiba Vendor Unique command */
#define GPCMD_PLAY_AUDIO_PIONEER                      0xc9 /* Pioneer Vendor Unique command */
#define GPCMD_PLAY_AUDIO_TRACK_RELATIVE_10_MATSUSHITA 0xc9 /* Matsushita Vendor Unique command */
#define GPCMD_PLAYBACK_CONTROL_SONY                   0xc9 /* Sony Vendor Unique command */
#define GPCMD_PAUSE_PIONEER                           0xca /* Pioneer Vendor Unique command */
#define GPCMD_PAUSE_RESUME_MATSUSHITA                 0xcb /* Matsushita Vendor Unique command */
#define GPCMD_STOP_PIONEER                            0xcb /* Pioneer Vendor Unique command */
#define GPCMD_PLAYBACK_STATUS_PIONEER                 0xcc /* Pioneer Vendor Unique command */
#define GPCMD_SCAN_PIONEER                            0xcd /* Should be equivalent to 0xba */
#define GPCMD_READ_CD_MSF_OLD                         0xd5 /* Should be equivalent to 0xb9 */
#define GPCMD_AUDIO_TRACK_SEARCH_NEC                  0xd8 /* NEC Vendor Unique command */
#define GPCMD_PLAY_AUDIO_NEC                          0xd9 /* NEC Vendor Unique command */
#define GPCMD_SET_SPEED_ALT                           0xda /* Should be equivalent to 0xbb */
#define GPCMD_STILL_NEC                               0xda /* NEC Vendor Unique command */
#define GPCMD_SET_STOP_TIME_NEC                       0xdb /* NEC Vendor Unique command */
#define GPCMD_CADDY_EJECT_NEC                         0xdc /* NEC Vendor Unique command */
#define GPCMD_READ_SUBCODEQ_PLAYING_STATUS_NEC        0xdd /* NEC Vendor Unique command */
#define GPCMD_READ_DISC_INFORMATION_NEC               0xde /* NEC Vendor Unique command */
#define GPCMD_DRIVE_STATUS_PIONEER                    0xe0 /* Pioneer Vendor Unique command */
#define GPCMD_PLAY_AUDIO_12_MATSUSHITA                0xe5 /* Matsushita Vendor Unique command */
#define GPCMD_PLAY_AUDIO_TRACK_RELATIVE_12_MATSUSHITA 0xe9 /* Matsushita Vendor Unique command */

/* Mode page codes for mode sense/set */
#define GPMODE_UNIT_ATN_PAGE               0x00
#define GPMODE_R_W_ERROR_PAGE              0x01
#define GPMODE_DISCONNECT_PAGE             0x02 /* Disconnect/reconnect page */
#define GPMODE_FORMAT_DEVICE_PAGE          0x03
#define GPMODE_RIGID_DISK_PAGE             0x04 /* Rigid disk geometry page */
#define GPMODE_FLEXIBLE_DISK_PAGE          0x05
#define GPMODE_CDROM_WRITE_PARAMETERS_PAGE 0x05
#define GPMODE_CACHING_PAGE                0x08
#define GPMODE_CDROM_PAGE_SONY             0x08
#define GPMODE_CDROM_AUDIO_PAGE_SONY       0x09
#define GPMODE_CDROM_PAGE                  0x0d
#define GPMODE_CDROM_AUDIO_PAGE            0x0e
#define GPMODE_DATA_COMPRESS_PAGE          0x0f
#define GPMODE_DEVICE_CONFIG_PAGE          0x10
#define GPMODE_MEDIUM_PARTITION_PAGE       0x11
#define GPMODE_CAPABILITIES_PAGE           0x2a
#define GPMODE_IOMEGA_PAGE                 0x2f
#define GPMODE_UNK_VENDOR_PAGE             0x30
#define GPMODE_ALL_PAGES                   0x3f

/* Mode page codes for presence */
#define GPMODEP_UNIT_ATN_PAGE         0x0000000000000001LL
#define GPMODEP_R_W_ERROR_PAGE        0x0000000000000002LL
#define GPMODEP_DISCONNECT_PAGE       0x0000000000000004LL
#define GPMODEP_FORMAT_DEVICE_PAGE    0x0000000000000008LL
#define GPMODEP_RIGID_DISK_PAGE       0x0000000000000010LL
#define GPMODEP_FLEXIBLE_DISK_PAGE    0x0000000000000020LL
#define GPMODEP_CACHING_PAGE          0x0000000000000100LL
#define GPMODEP_CDROM_PAGE_SONY       0x0000000000000100LL
#define GPMODEP_CDROM_AUDIO_PAGE_SONY 0x0000000000000200LL
#define GPMODEP_CDROM_PAGE            0x0000000000002000LL
#define GPMODEP_CDROM_AUDIO_PAGE      0x0000000000004000LL
#define GPMODEP_DATA_COMPRESS_PAGE    0x0000000000008000LL
#define GPMODEP_DEVICE_CONFIG_PAGE    0x0000000000010000LL
#define GPMODEP_MEDIUM_PARTITION_PAGE 0x0000000000020000LL
#define GPMODEP_CAPABILITIES_PAGE     0x0000040000000000LL
#define GPMODEP_IOMEGA_PAGE           0x0000800000000000LL
#define GPMODEP_UNK_VENDOR_PAGE       0x0001000000000000LL
#define GPMODEP_ALL_PAGES             0x8000000000000000LL

/* SCSI Status Codes */
#define SCSI_STATUS_OK              0
#define SCSI_STATUS_CHECK_CONDITION 2

/* SCSI Sense Keys */
#define SENSE_NONE            0
#define SENSE_NOT_READY       2
#define SENSE_MEDIUM_ERROR    3
#define SENSE_ILLEGAL_REQUEST 5
#define SENSE_UNIT_ATTENTION  6
#define SENSE_DATA_PROTECT    7
#define SENSE_BLANK_CHECK     8

/* SCSI Additional Sense Codes */
#define ASC_NONE                               0x00
#define ASC_AUDIO_PLAY_OPERATION               0x00
#define ASC_NOT_READY                          0x04
#define ASC_WRITE_ERROR                        0x0c
#define ASC_UNRECOVERED_READ_ERROR             0x11
#define ASC_ILLEGAL_OPCODE                     0x20
#define ASC_LBA_OUT_OF_RANGE                   0x21
#define ASC_INV_FIELD_IN_CMD_PACKET            0x24
#define ASC_INV_LUN                            0x25
#define ASC_INV_FIELD_IN_PARAMETER_LIST        0x26
#define ASC_WRITE_PROTECTED                    0x27
#define ASC_MEDIUM_MAY_HAVE_CHANGED            0x28
#define ASC_CAPACITY_DATA_CHANGED              0x2a
#define ASC_INCOMPATIBLE_FORMAT                0x30
#define ASC_MEDIUM_NOT_PRESENT                 0x3a
#define ASC_DATA_PHASE_ERROR                   0x4b
#define ASC_ILLEGAL_MODE_FOR_THIS_TRACK        0x64

#define ASCQ_NONE                              0x00
#define ASCQ_UNIT_IN_PROCESS_OF_BECOMING_READY 0x01
#define ASCQ_INITIALIZING_COMMAND_REQUIRED     0x02
#define ASCQ_CIRC_UNRECOVERED_ERROR            0x06
#define ASCQ_CAPACITY_DATA_CHANGED             0x09
#define ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS  0x11
#define ASCQ_AUDIO_PLAY_OPERATION_PAUSED       0x12
#define ASCQ_AUDIO_PLAY_OPERATION_COMPLETED    0x13
#define ASCQ_FILEMARK_DETECTED                 0x01
#define ASCQ_EOM_DETECTED                      0x02
#define ASCQ_BOP_DETECTED                      0x04
#define ASCQ_EOD_DETECTED                      0x05

/* Event notification classes for GET EVENT STATUS NOTIFICATION */
#define GESN_NO_EVENTS          0
#define GESN_OPERATIONAL_CHANGE 1
#define GESN_POWER_MANAGEMENT   2
#define GESN_EXTERNAL_REQUEST   3
#define GESN_MEDIA              4
#define GESN_MULTIPLE_HOSTS     5
#define GESN_DEVICE_BUSY        6

/* Event codes for MEDIA event status notification */
#define MEC_NO_CHANGE           0
#define MEC_EJECT_REQUESTED     1
#define MEC_NEW_MEDIA           2
#define MEC_MEDIA_REMOVAL       3 /* only for media changers */
#define MEC_MEDIA_CHANGED       4 /* only for media changers */
#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */
#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */
#define MS_TRAY_OPEN            1
#define MS_MEDIA_PRESENT        2

/*
 * The MMC values are not IDE specific and might need to be moved
 * to a common header if they are also needed for the SCSI emulation
 */

/* Profile list from MMC-6 revision 1 table 91 */
#define MMC_PROFILE_NONE              0x0000
#define MMC_PROFILE_REMOVABLE_DISK    0x0002
#define MMC_PROFILE_MO                0x0003
#define MMC_PROFILE_MO_WORM           0x0004
#define MMC_PROFILE_AS_MO             0x0005
#define MMC_PROFILE_CD_ROM            0x0008
#define MMC_PROFILE_CD_R              0x0009
#define MMC_PROFILE_CD_RW             0x000A
#define MMC_PROFILE_DVD_ROM           0x0010
#define MMC_PROFILE_DVD_R_SR          0x0011
#define MMC_PROFILE_DVD_RAM           0x0012
#define MMC_PROFILE_DVD_RW_RO         0x0013
#define MMC_PROFILE_DVD_RW_SR         0x0014
#define MMC_PROFILE_DVD_R_DL_SR       0x0015
#define MMC_PROFILE_DVD_R_DL_JR       0x0016
#define MMC_PROFILE_DVD_RW_DL         0x0017
#define MMC_PROFILE_DVD_DDR           0x0018
#define MMC_PROFILE_DVD_PLUS_RW       0x001A
#define MMC_PROFILE_DVD_PLUS_R        0x001B
#define MMC_PROFILE_DVD_PLUS_RW_DL    0x002A
#define MMC_PROFILE_DVD_PLUS_R_DL     0x002B
#define MMC_PROFILE_BD_ROM            0x0040
#define MMC_PROFILE_BD_R_SRM          0x0041
#define MMC_PROFILE_BD_R_RRM          0x0042
#define MMC_PROFILE_BD_RE             0x0043
#define MMC_PROFILE_HDDVD_ROM         0x0050
#define MMC_PROFILE_HDDVD_R           0x0051
#define MMC_PROFILE_HDDVD_RAM         0x0052
#define MMC_PROFILE_HDDVD_RW          0x0053
#define MMC_PROFILE_HDDVD_R_DL        0x0058
#define MMC_PROFILE_HDDVD_RW_DL       0x005A
#define MMC_PROFILE_INVALID           0xFFFF

#define SCSI_ONLY                     32
#define ATAPI_ONLY                    16
#define IMPLEMENTED                   8
#define NONDATA                       4
#define CHECK_READY                   2
#define ALLOW_UA                      1

#define MSG_COMMAND_COMPLETE          0x00

#define BUS_DBP                       0x01
#define BUS_SEL                       0x02
#define BUS_IO                        0x04
#define BUS_CD                        0x08
#define BUS_MSG                       0x10
#define BUS_REQ                       0x20
#define BUS_BSY                       0x40
#define BUS_RST                       0x80
#define BUS_ACK                       0x100
#define BUS_ATN                       0x200
#define BUS_ARB                       0x8000
#define BUS_SETDATA(val)              ((uint32_t) val << 16)
#define BUS_GETDATA(val)              ((val >> 16) & 0xff)
#define BUS_DATAMASK                  0xff0000

#define BUS_IDLE                      (1 << 31)

#define STATE_IDLE                    0
#define STATE_COMMAND                 1
#define STATE_DATAIN                  2
#define STATE_DATAOUT                 3
#define STATE_STATUS                  4
#define STATE_MESSAGEIN               5
#define STATE_SELECT                  6
#define STATE_MESSAGEOUT              7
#define STATE_MESSAGE_ID              8

#define PIO_TX_BUS                    0
#define DMA_IN_TX_BUS                 1
#define DMA_OUT_TX_BUS                2

#define PHASE_IDLE                    0x00
#define PHASE_COMMAND                 0x01
#define PHASE_DATA_IN                 0x02
#define PHASE_DATA_OUT                0x03
#define PHASE_DATA_IN_DMA             0x04
#define PHASE_DATA_OUT_DMA            0x05
#define PHASE_COMPLETE                0x06
#define PHASE_ERROR                   0x80
#define PHASE_NONE                    0xff

#define SCSI_PHASE_DATA_OUT           0
#define SCSI_PHASE_DATA_IN            BUS_IO
#define SCSI_PHASE_COMMAND            BUS_CD
#define SCSI_PHASE_STATUS             (BUS_CD | BUS_IO)
#define SCSI_PHASE_MESSAGE_OUT        (BUS_MSG | BUS_CD)
#define SCSI_PHASE_MESSAGE_IN         (BUS_MSG | BUS_CD | BUS_IO)

#define MODE_SELECT_PHASE_IDLE        0
#define MODE_SELECT_PHASE_HEADER      1
#define MODE_SELECT_PHASE_BLOCK_DESC  2
#define MODE_SELECT_PHASE_PAGE_HEADER 3
#define MODE_SELECT_PHASE_PAGE        4

typedef struct mode_sense_pages_t {
    uint8_t            pages[0x40][0x40];
} mode_sense_pages_t;

/* This is so we can access the common elements to all SCSI device structs
   without knowing the device type. */
typedef struct scsi_common_s {
    mode_sense_pages_t ms_pages_saved;

    void *             priv;
#ifdef EMU_IDE_H
    ide_tf_t *         tf;
#else
    void *             tf;
#endif

    void *             log;

    uint8_t *          temp_buffer;
    size_t             temp_buffer_sz;
    /*
       This is atapi_cdb in ATAPI-supporting devices,
       and pad in SCSI-only devices.
     */
    uint8_t            atapi_cdb[16];
    uint8_t            current_cdb[16];
    uint8_t            sense[256];

    uint8_t            id;
    uint8_t            cur_lun;
    uint8_t            pad0;
    uint8_t            pad1;

    uint16_t           max_transfer_len;
    uint16_t           pad2;

    int                requested_blocks;
    int                packet_status;
    int                total_length;
    int                do_page_save;
    int                unit_attention;
    int                request_pos;
    int                wait;
    int                buffer_pos;

    uint32_t           sector_pos;
    uint32_t           sector_len;
    uint32_t           packet_len;
    uint32_t           block_len;

    double             callback;

    uint8_t            (*ven_cmd)(void *sc, uint8_t *cdb, int32_t *BufLen);
} scsi_common_t;

typedef struct scsi_device_t {
    int32_t            buffer_length;

    uint8_t            status;
    uint8_t            phase;

    uint16_t           type;

    scsi_common_t *    sc;

    void               (*command)(scsi_common_t *sc, const uint8_t *cdb);
    void               (*request_sense)(scsi_common_t *sc, uint8_t *buffer,
                                        uint8_t alloc_length);
    void               (*reset)(scsi_common_t *sc);
    uint8_t            (*phase_data_out)(scsi_common_t *sc);
    void               (*command_stop)(scsi_common_t *sc);
} scsi_device_t;

typedef struct scsi_bus_t {
    uint8_t            data;
    uint8_t            msglun;
    uint8_t            data_wait;
    uint8_t            target_id;
    uint8_t            bus_device;
    uint8_t            pad;
    uint8_t            pad0;
    uint8_t            pad1;

    uint8_t            command[16];
    uint8_t            msgout[4];
    uint8_t            pad2[4];

    int                tx_mode;
    int                clear_req;
    int                wait_data;
    int                wait_complete;
    int                bus_out;
    int                bus_in;
    int                command_pos;
    int                command_issued;
    int                data_pos;
    int                msgout_pos;
    int                is_msgout;
    int                state;

    uint32_t           bus_phase;
    uint32_t           total_len;
    uint32_t           data_repeat;

    double             period;
    double             speed;
    double             divider;
    double             multi;

    void              *priv;
    void               (*timer)(void *priv, double period);
} scsi_bus_t;

/* These are based on the INQUIRY values. */
#define SCSI_NONE            0x0060
#define SCSI_FIXED_DISK      0x0000
#define SCSI_REMOVABLE_DISK  0x8000
#define SCSI_REMOVABLE_TAPE  0x8001
#define SCSI_REMOVABLE_CDROM 0x8005

#ifdef EMU_SCSI_H
extern scsi_device_t scsi_devices[SCSI_BUS_MAX][SCSI_ID_MAX];
#endif /* EMU_SCSI_H */

extern double   scsi_device_get_callback(scsi_device_t *dev);
extern uint8_t *scsi_device_sense(scsi_device_t *dev);
extern void     scsi_device_request_sense(scsi_device_t *dev, uint8_t *buffer,
                                          uint8_t alloc_length);
extern void     scsi_device_reset(scsi_device_t *dev);
extern int      scsi_device_present(scsi_device_t *dev);
extern int      scsi_device_valid(scsi_device_t *dev);
extern int      scsi_device_cdb_length(scsi_device_t *dev);
extern void     scsi_device_command_phase0(scsi_device_t *dev, uint8_t *cdb);
extern void     scsi_device_command_stop(scsi_device_t *dev);
extern void     scsi_device_command_phase1(scsi_device_t *dev);
extern void     scsi_device_identify(scsi_device_t *dev, uint8_t lun);
extern void     scsi_device_close_all(void);
extern void     scsi_device_init(void);

extern void    scsi_reset(void);
extern uint8_t scsi_get_bus(void);
extern int     scsi_bus_read(scsi_bus_t *scsi_bus);
extern void    scsi_bus_update(scsi_bus_t *scsi_bus, int bus);

extern void    scsi_bus_set_speed(uint8_t bus, double speed);
extern double  scsi_bus_get_speed(uint8_t bus);

#endif /*SCSI_DEVICE_H*/
