#define DBG 1

#include "scsi.h"


//
// Device Extension Device Flags
//

#define DFLAGS_DEVICE_PRESENT        0x0001    // Indicates that some device is present.
#define DFLAGS_ATAPI_DEVICE          0x0002    // Indicates whether Atapi commands can be used.
#define DFLAGS_TAPE_DEVICE           0x0004    // Indicates whether this is a tape device.
#define DFLAGS_INT_DRQ               0x0008    // Indicates whether device interrupts as DRQ is set after
                                               // receiving Atapi Packet Command
#define DFLAGS_REMOVABLE_DRIVE       0x0010    // Indicates that the drive has the 'removable' bit set in
                                               // identify data (offset 128)
#define DFLAGS_MEDIA_STATUS_ENABLED  0x0020    // Media status notification enabled
#define DFLAGS_ATAPI_CHANGER         0x0040    // Indicates atapi 2.5 changer present.
#define DFLAGS_SANYO_ATAPI_CHANGER   0x0080    // Indicates multi-platter device, not conforming to the 2.5 spec.
#define DFLAGS_CHANGER_INITED        0x0100    // Indicates that the init path for changers has already been done.

//
// Addreses of IDE bus, used only bits 17,16 and 3..0
//
#define IDE_DATA                0x1f0
#define IDE_ERROR               0x1f1
#define IDE_BLOCK_COUNT         0x1f2
#define IDE_BLOCK_NUMBER        0x1f3
#define IDE_CYLINDER_LOW        0x1f4
#define IDE_CYLINDER_HIGH       0x1f5
#define IDE_DRIVE_HEAD          0x1f6
#define IDE_COMMAND             0x1f7
#define IDE_STATUS              0x1f7
#define IDE_ALTERNATE_STATUS    0x3f6
#define IDE_DRIVE_CONTROL       0x3f6

#define MAX_ERRORS                     4

//
// ATAPI command definitions
//

#define ATAPI_MODE_SENSE   0x5A
#define ATAPI_MODE_SELECT  0x55
#define ATAPI_FORMAT_UNIT  0x24

//
// ATAPI register definition
//

#define ATAPI_DATA              0x1f0
#define ATAPI_INTERRUPT_REASON  0x1f2
#define ATAPI_UNUSED_1          0x1f3
#define ATAPI_BYTE_COUNT_LOW    0x1f4
#define ATAPI_BYTE_COUNT_HIGH   0x1f5
#define ATAPI_DRIVE_SELECT      0x1f6
#define ATAPI_COMMAND           0x1f7
#define ATAPI_ALTERNATE_STATUS  0x3f6
#define ATAPI_DRIVE_CONTROL     0x3f6


//
// IDE command definitions
//

#define IDE_COMMAND_ATAPI_RESET      0x08
#define IDE_COMMAND_RECALIBRATE      0x10
#define IDE_COMMAND_READ             0x20
#define IDE_COMMAND_WRITE            0x30
#define IDE_COMMAND_VERIFY           0x40
#define IDE_COMMAND_SEEK             0x70
#define IDE_COMMAND_SET_DRIVE_PARAMETERS 0x91
#define IDE_COMMAND_ATAPI_PACKET     0xA0
#define IDE_COMMAND_ATAPI_IDENTIFY   0xA1
#define IDE_COMMAND_READ_MULTIPLE    0xC4
#define IDE_COMMAND_WRITE_MULTIPLE   0xC5
#define IDE_COMMAND_SET_MULTIPLE     0xC6
#define IDE_COMMAND_READ_DMA         0xC8
#define IDE_COMMAND_WRITE_DMA             0xCA
#define IDE_COMMAND_GET_MEDIA_STATUS      0xDA
#define IDE_COMMAND_ENABLE_MEDIA_STATUS   0xEF
#define IDE_COMMAND_IDENTIFY              0xEC
#define IDE_COMMAND_MEDIA_EJECT           0xED

//
// IDE status definitions
//

#define IDE_STATUS_ERROR             0x01
#define IDE_STATUS_INDEX             0x02
#define IDE_STATUS_CORRECTED_ERROR   0x04
#define IDE_STATUS_DRQ               0x08
#define IDE_STATUS_DSC               0x10
#define IDE_STATUS_DRDY              0x40
#define IDE_STATUS_IDLE              0x50
#define IDE_STATUS_BUSY              0x80

//
// IDE drive select/head definitions
//

#define IDE_DRIVE_SELECT_1           0xA0
#define IDE_DRIVE_SELECT_2           0x10

//
// IDE drive control definitions
//

#define IDE_DC_DISABLE_INTERRUPTS    0x02
#define IDE_DC_RESET_CONTROLLER      0x04
#define IDE_DC_REENABLE_CONTROLLER   0x00

//
// IDE error definitions
//

#define IDE_ERROR_BAD_BLOCK          0x80
#define IDE_ERROR_DATA_ERROR         0x40
#define IDE_ERROR_MEDIA_CHANGE       0x20
#define IDE_ERROR_ID_NOT_FOUND       0x10
#define IDE_ERROR_MEDIA_CHANGE_REQ   0x08
#define IDE_ERROR_COMMAND_ABORTED    0x04
#define IDE_ERROR_END_OF_MEDIA       0x02
#define IDE_ERROR_ILLEGAL_LENGTH     0x01


//
// IDENTIFY data
//

typedef struct _IDENTIFY_DATA {
    USHORT GeneralConfiguration;            // 00 00
    USHORT NumberOfCylinders;               // 02  1
    USHORT Reserved1;                       // 04  2
    USHORT NumberOfHeads;                   // 06  3
    USHORT UnformattedBytesPerTrack;        // 08  4
    USHORT UnformattedBytesPerSector;       // 0A  5
    USHORT SectorsPerTrack;                 // 0C  6
    USHORT VendorUnique1[3];                // 0E  7-9
    USHORT SerialNumber[10];                // 14  10-19
    USHORT BufferType;                      // 28  20
    USHORT BufferSectorSize;                // 2A  21
    USHORT NumberOfEccBytes;                // 2C  22
    USHORT FirmwareRevision[4];             // 2E  23-26
    USHORT ModelNumber[20];                 // 36  27-46
    UCHAR  MaximumBlockTransfer;            // 5E  47
    UCHAR  VendorUnique2;                   // 5F
    USHORT DoubleWordIo;                    // 60  48
    USHORT Capabilities;                    // 62  49
    USHORT Reserved2;                       // 64  50
    UCHAR  VendorUnique3;                   // 66  51
    UCHAR  PioCycleTimingMode;              // 67
    UCHAR  VendorUnique4;                   // 68  52
    UCHAR  DmaCycleTimingMode;              // 69
    USHORT TranslationFieldsValid:1;        // 6A  53
    USHORT Reserved3:15;
    USHORT NumberOfCurrentCylinders;        // 6C  54
    USHORT NumberOfCurrentHeads;            // 6E  55
    USHORT CurrentSectorsPerTrack;          // 70  56
    ULONG  CurrentSectorCapacity;           // 72  57-58
    USHORT CurrentMultiSectorSetting;       //     59
    ULONG  UserAddressableSectors;          //     60-61
    USHORT SingleWordDMASupport : 8;        //     62
    USHORT SingleWordDMAActive : 8;
    USHORT MultiWordDMASupport : 8;         //     63
    USHORT MultiWordDMAActive : 8;
    USHORT AdvancedPIOModes : 8;            //     64
    USHORT Reserved4 : 8;
    USHORT MinimumMWXferCycleTime;          //     65
    USHORT RecommendedMWXferCycleTime;      //     66
    USHORT MinimumPIOCycleTime;             //     67
    USHORT MinimumPIOCycleTimeIORDY;        //     68
    USHORT Reserved5[2];                    //     69-70
    USHORT ReleaseTimeOverlapped;           //     71
    USHORT ReleaseTimeServiceCommand;       //     72
    USHORT MajorRevision;                   //     73
    USHORT MinorRevision;                   //     74
    USHORT Reserved6[50];                   //     75-126
    USHORT SpecialFunctionsEnabled;         //     127
    USHORT Reserved7[128];                  //     128-255
} IDENTIFY_DATA, *PIDENTIFY_DATA;

//
// Identify data without the Reserved4.
//

typedef struct _IDENTIFY_DATA2 {
    USHORT GeneralConfiguration;            // 00
    USHORT NumberOfCylinders;               // 02
    USHORT Reserved1;                       // 04
    USHORT NumberOfHeads;                   // 06
    USHORT UnformattedBytesPerTrack;        // 08
    USHORT UnformattedBytesPerSector;       // 0A
    USHORT SectorsPerTrack;                 // 0C
    USHORT VendorUnique1[3];                // 0E
    USHORT SerialNumber[10];                // 14
    USHORT BufferType;                      // 28
    USHORT BufferSectorSize;                // 2A
    USHORT NumberOfEccBytes;                // 2C
    USHORT FirmwareRevision[4];             // 2E
    USHORT ModelNumber[20];                 // 36
    UCHAR  MaximumBlockTransfer;            // 5E
    UCHAR  VendorUnique2;                   // 5F
    USHORT DoubleWordIo;                    // 60
    USHORT Capabilities;                    // 62
    USHORT Reserved2;                       // 64
    UCHAR  VendorUnique3;                   // 66
    UCHAR  PioCycleTimingMode;              // 67
    UCHAR  VendorUnique4;                   // 68
    UCHAR  DmaCycleTimingMode;              // 69
    USHORT TranslationFieldsValid:1;        // 6A
    USHORT Reserved3:15;
    USHORT NumberOfCurrentCylinders;        // 6C
    USHORT NumberOfCurrentHeads;            // 6E
    USHORT CurrentSectorsPerTrack;          // 70
    ULONG  CurrentSectorCapacity;           // 72
} IDENTIFY_DATA2, *PIDENTIFY_DATA2;

//
// Beautification macros
//

#define GetStatus( LptBase, Status) \
    Status = Ide2LptInpByte( LptBase, IDE_ALTERNATE_STATUS );

#define GetBaseStatus( LptBase, Status ) \
    Status = Ide2LptInpByte( LptBase, IDE_STATUS );

#define WriteCommand( LptBase, Command ) \
    Ide2LptOutByte( LptBase, IDE_COMMAND, Command );

#define ReadBuffer( LptBase, Buffer, Count )\
{\
    ULONG i;\
    for( i=0; i!=Count; i++ ){\
        Buffer[i] = Ide2LptInpWord( LptBase );\
    }\
}

#define WriteBuffer( LptBase, Buffer, Count )\
{\
    ULONG i;\
    for( i=0; i!=Count; i++ ){\
        Ide2LptOutWord( LptBase, Buffer[i] );\
    }\
}

#define WaitOnBusy( LptBase, Status ) \
{ \
    ULONG i; \
    for (i=0; i<2000; i++) { \
        GetStatus( LptBase, Status ); \
        if (Status & IDE_STATUS_BUSY) { \
            ScsiPortStallExecution(150); \
            continue; \
        } else { \
            break; \
        } \
    } \
}

#define WaitOnBaseBusy( LptBase, Status) \
{ \
    ULONG i; \
    for (i=0; i<2000; i++) { \
        GetBaseStatus( LptBase, Status ); \
        if (Status & IDE_STATUS_BUSY) { \
            ScsiPortStallExecution(150); \
            continue; \
        } else { \
            break; \
        } \
    } \
}

#define WaitForDrq( LptBase, Status ) \
{ \
    ULONG i; \
    for (i=0; i<100; i++) { \
        GetStatus( LptBase, Status ); \
        if (Status & IDE_STATUS_BUSY) { \
            ScsiPortStallExecution(100); \
        } else if (Status & IDE_STATUS_DRQ) { \
            break; \
        } else { \
            ScsiPortStallExecution(200); \
        } \
    } \
}


#define WaitShortForDrq( LptBase, Status ) \
{ \
    ULONG i; \
    for (i=0; i<2; i++) { \
        GetStatus( LptBase, Status ); \
        if (Status & IDE_STATUS_BUSY) { \
            ScsiPortStallExecution(100); \
        } else if (Status & IDE_STATUS_DRQ) { \
            break; \
        } else { \
            ScsiPortStallExecution(100); \
        } \
    } \
}

#define AtapiSoftReset( LptBase ) \
{\
    UCHAR statusByte; \
    Ide2LptOutByte( LptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 ); \
    ScsiPortStallExecution(500);\
    Ide2LptOutByte( LptBase, IDE_COMMAND, IDE_COMMAND_ATAPI_RESET); \
    ScsiPortStallExecution(1000*1000);\
    Ide2LptOutByte( LptBase, IDE_DRIVE_HEAD, IDE_DRIVE_SELECT_1 ); \
    WaitOnBusy( LptBase, statusByte ); \
    ScsiPortStallExecution(500);\
}

#define IdeHardReset( LptBase, result ) \
{\
    UCHAR statusByte;\
    ULONG i;\
    Ide2LptOutByte( LptBase, IDE_DRIVE_CONTROL, IDE_DC_RESET_CONTROLLER );\
    ScsiPortStallExecution(50 * 1000);\
    Ide2LptOutByte( LptBase, IDE_DRIVE_CONTROL, (IDE_DC_REENABLE_CONTROLLER|IDE_DC_DISABLE_INTERRUPTS) );\
    for (i = 0; i < 1000 * 1000; i++) {\
        statusByte = Ide2LptInpByte( LptBase, IDE_ALTERNATE_STATUS );\
        if (statusByte != IDE_STATUS_IDLE && statusByte != 0x0) {\
            ScsiPortStallExecution(5);\
        } else {\
            break;\
        }\
    }\
    if (i == 1000*1000) {\
        result = FALSE;\
    }\
    result = TRUE;\
}

#define IS_RDP(OperationCode)\
    ((OperationCode == SCSIOP_ERASE)||\
    (OperationCode == SCSIOP_LOAD_UNLOAD)||\
    (OperationCode == SCSIOP_LOCATE)||\
    (OperationCode == SCSIOP_REWIND) ||\
    (OperationCode == SCSIOP_SPACE)||\
    (OperationCode == SCSIOP_SEEK)||\
    (OperationCode == SCSIOP_WRITE_FILEMARKS))
