#ifndef _SCSI_H_
#define _SCSI_H_

struct scsi_bus_t;
struct atapi_device_t;

typedef struct scsi_device_t {
	void *(*init)(struct scsi_bus_t *bus, int id);
	void *(*atapi_init)(struct scsi_bus_t *bus, int id, struct atapi_device_t *atapi_dev);
	void (*close)(void *p);
	void (*reset)(void *p);

	void (*start_command)(void *p);
	int (*command)(uint8_t *cdb, void *p);

	uint8_t (*get_status)(void *p);
	uint8_t (*get_sense_key)(void *p);
	int (*get_bytes_required)(void *p);

	void (*atapi_identify)(uint16_t *buffer, void *p);
	int (*atapi_set_feature)(uint8_t feature, uint8_t val, void *p);

	uint8_t (*read)(void *p);
	void (*write)(uint8_t val, void *p);
	int (*read_complete)(void *p);
	int (*write_complete)(void *p);
} scsi_device_t;

#define CDB_MAX_LEN 20

typedef struct scsi_bus_t {
	int state;
	int new_state;
	int clear_req;
	uint32_t bus_in, bus_out;
	int dev_id;

	int command_pos;
	uint8_t command[CDB_MAX_LEN];

	scsi_device_t *devices[8];
	void *device_data[8];

	int change_state_delay;
	int new_req_delay;

	int is_atapi;
} scsi_bus_t;

//int scsi_add_data(uint8_t val);
//int scsi_get_data();
void scsi_set_phase(uint8_t phase);
void scsi_set_irq(uint8_t status);

#define SCSI_TEST_UNIT_READY              0x00
#define SCSI_REZERO_UNIT                  0x01
#define SCSI_REQUEST_SENSE                0x03
#define SCSI_FORMAT                       0x04
#define SCSI_READ_6                       0x08
#define SCSI_WRITE_6                      0x0a
#define SCSI_SEEK_6                       0x0b
#define SCSI_INQUIRY                      0x12
#define SCSI_MODE_SELECT_6                0x15
#define SCSI_MODE_SENSE_6                 0x1a
#define SCSI_START_STOP_UNIT              0x1b
#define SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
#define SCSI_READ_CAPACITY_10             0x25
#define SCSI_READ_10                      0x28
#define SCSI_WRITE_10                     0x2a
#define SCSI_SEEK_10                      0x2b
#define SCSI_WRITE_AND_VERIFY             0x2e
#define SCSI_VERIFY_10                    0x2f
#define SCSI_READ_BUFFER                  0x3c
#define SCSI_MODE_SENSE_10                0x5a

#define SCSI_RESERVE                      0x16
#define SCSI_RELEASE                      0x17
#define SEND_DIAGNOSTIC                   0x1d

#define STATUS_GOOD            0x00
#define STATUS_CHECK_CONDITION 0x02

#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 0x200
#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)

int scsi_bus_update(scsi_bus_t *bus, int bus_assert);
int scsi_bus_read(scsi_bus_t *bus);
int scsi_bus_match(scsi_bus_t *bus, int bus_assert);
void scsi_bus_kick(scsi_bus_t *bus);
void scsi_bus_init(scsi_bus_t *bus);
void scsi_bus_close(scsi_bus_t *bus);
void scsi_bus_reset(scsi_bus_t *bus);

void scsi_bus_atapi_init(scsi_bus_t *bus, scsi_device_t *device, int id, struct atapi_device_t *atapi_dev);

#define KEY_NONE                        0
#define KEY_NOT_READY                        2
#define KEY_ILLEGAL_REQ                 5
#define KEY_UNIT_ATTENTION                6
#define KEY_DATA_PROTECT                7

#define ASC_AUDIO_PLAY_OPERATION        0x00
#define ASC_ILLEGAL_OPCODE                0x20
#define        ASC_LBA_OUT_OF_RANGE            0x21
#define        ASC_INV_FIELD_IN_CMD_PACKET        0x24
#define ASC_INVALID_LUN                 0x25
#define ASC_WRITE_PROTECT               0x27
#define ASC_MEDIUM_MAY_HAVE_CHANGED        0x28
#define ASC_MEDIUM_NOT_PRESENT                0x3a
#define ASC_DATA_PHASE_ERROR                0x4b
#define ASC_ILLEGAL_MODE_FOR_THIS_TRACK        0x64

#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 START_STOP_START 0x01
#define START_STOP_LOEJ  0x02

#endif /*_SCSI_H_*/
