// Aspi.cpp: implementation of the aspi methods
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "cdvd.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define MOVESCSIDWORD(pdwSrc,pdwDst) \
{\
    ((PBYTE)(pdwDst))[0] = ((PBYTE)(pdwSrc))[3];\
    ((PBYTE)(pdwDst))[1] = ((PBYTE)(pdwSrc))[2];\
    ((PBYTE)(pdwDst))[2] = ((PBYTE)(pdwSrc))[1];\
    ((PBYTE)(pdwDst))[3] = ((PBYTE)(pdwSrc))[0];\
}

#define MOVESCSIWORD(pwSrc,pwDst) \
{\
    ((PBYTE)(pwDst))[0] = ((PBYTE)(pwSrc))[1];\
    ((PBYTE)(pwDst))[1] = ((PBYTE)(pwSrc))[0];\
}


/***********************************************************/
/*              ASPI Init/Shutdown related methods         */
/***********************************************************/

// using MFC classes, no aspi in other systems.
int CCdvd::Aspi_CheckValidInstallation()
{
    int retval = CDVD_ERROR_FAIL;

	CFileStatus fstat;
	TCHAR szDir[MAX_PATH];
	
	if(GetSystemDirectory(szDir,MAX_PATH))
	{
		CString directory = szDir;
		WIN32OSTYPE ostype = GetWin32OSType();

		if(ostype != WINNTNEW && ostype != WINNTOLD)
		{
			if(CFile::GetStatus(directory+_T("\\wnaspi32.dll"), fstat) && 
				CFile::GetStatus(directory+_T("\\winaspi.dll"), fstat) &&
				CFile::GetStatus(directory+_T("\\iosubsys\\apix.vxd"), fstat))
            {
                retval = CDVD_ERROR_SUCCESS;
            }
		}
		else
		{
			if(CFile::GetStatus(directory+_T("\\wnaspi32.dll"), fstat) && 
			   CFile::GetStatus(directory+_T("\\drivers\\aspi32.sys"), fstat))
            {
                retval = CDVD_ERROR_SUCCESS;
            }
		}
	}
    else
    {
        // are you crazy? o_O, no fucking windows directory? o_O
        retval = CDVD_ERROR_FAIL;
    }

	return retval;

}

// called once to init Aspi
int CCdvd::Aspi_Init()
{
    int retval = CDVD_ERROR_FAIL;
    if(m_bAspiInitialized) 
        return CDVD_ERROR_SUCCESS;

    if(Aspi_CheckValidInstallation() == CDVD_ERROR_SUCCESS)
    {
        if((m_hAspi = AfxLoadLibrary("WNASPI32")) != NULL)
        {
        	SendASPI32Command    = (CDROMSendASPI32Command)    GetProcAddress(m_hAspi,"SendASPI32Command");
        	GetASPI32SupportInfo = (CDROMGetASPI32SupportInfo) GetProcAddress(m_hAspi,"GetASPI32SupportInfo");
            
            if(SendASPI32Command != NULL &&
               GetASPI32SupportInfo != NULL)
            {
                UI32 support_info = GetASPI32SupportInfo();
                if( HIBYTE(LOWORD(support_info)) != SS_COMP &&
                    HIBYTE(LOWORD(support_info)) != SS_NO_ADAPTERS )
                {
                    Aspi_Shutdown();              
                }
                else
                {
                    if(InitializeBuffers() == CDVD_ERROR_SUCCESS)
                    {
                        if(Aspi_ScsiBusScan() == CDVD_ERROR_SUCCESS)
                        {
                            SetCurrentBuffer(0);
                            m_bAspiInitialized = TRUE;
                            retval = CDVD_ERROR_SUCCESS;
                            GetNumSectors = Aspi_GetNumSectors;
                            GetToc        = Aspi_GetToc;
                        //  GetHeader     = Aspi_GetHeader;
                            SetSpeed      = Aspi_SetSpeed;
                            Stop          = Aspi_Stop;
                            SetSectorSize = Aspi_SetSectorSize;
                            Play          = Aspi_Play;
                            TestReady     = Aspi_TestReady;
                            ReadSector    = Dummy_T4;
                            UpdateInterfaceObject();
                        }
                    }
                }
            }
            else
            {
                Aspi_Shutdown();
            }
        }
    }

    return retval;
}

// shuts down aspi
int CCdvd::Aspi_Shutdown()
{
    int retval = CDVD_ERROR_SUCCESS;

    // gota detect the read_mode first before i do this
    // but just reset to 2048 for now.
    Aspi_SetSectorSize(CDVD_SECTOR_SIZE_DVD);

    // the old ASPI drivers suck shit to hell and damnation,
    // freeing them up immediately after init will crash sometimes
    ::Sleep(200); 

    if(m_hAspi != NULL) 
        AfxFreeLibrary(m_hAspi);
    m_hAspi = NULL;

    SendASPI32Command    = NULL;
    GetASPI32SupportInfo = NULL;               

    GetNumSectors = Dummy_T1;
    GetToc        = Dummy_T1;
    Stop          = Dummy_T1;
    Play          = Dummy_T3;
    TestReady     = Dummy_T1;
//  GetHeader     = Dummy_T1;
    SetSpeed      = Dummy_T2;
    SetSectorSize = Dummy_T2;
    ReadSector    = Dummy_T4;
    UpdateInterfaceObject();

    ShutdownBuffers();

    m_bAspiInitialized = FALSE;

    return retval;
}

// open a drive for reading
int CCdvd::Aspi_OpenDrive(int drv_num)
{
    int retval = CDVD_ERROR_SUCCESS;
    CloseDrive();
    m_nCurrentDrive = drv_num;
    
    return retval;
}

// dummy for now
int CCdvd::Aspi_CloseDrive()
{
    int retval = CDVD_ERROR_SUCCESS;
    m_nCurrentDrive = -1;

    return retval;
}

// get srb status
int CCdvd::Aspi_GetSrbStatus(int srb_num)
{
    int retval = CDVD_SRB_ERROR;
    switch(m_nCurrentReadMode)
    {
    case CDVD_READ_MMC:    retval = m_srbReadCd  [srb_num].SRB_Status; break;
    case CDVD_READ_SCSI10: retval = m_srbRead10  [srb_num].SRB_Status; break;
    case CDVD_READ_D8:     retval = m_srbReadMat [srb_num].SRB_Status; break;      
    case CDVD_READ_D410:   retval = m_srbReadSony[srb_num].SRB_Status; break;
    case CDVD_READ_D412:   retval = m_srbReadNec [srb_num].SRB_Status; break;
    default: break;
    }

    if(retval == SS_COMP) retval = CDVD_SRB_COMPLETED;
    else if(retval == SS_PENDING) retval = CDVD_SRB_PENDING;
    else //if(retval == SS_ERR) 
        retval = CDVD_SRB_ERROR;

    return retval;
}

// set read mode
int CCdvd::Aspi_SetReadMode(CDVD_READ_MODE read_mode)
{
    int retval = CDVD_ERROR_SUCCESS;

    switch(read_mode)
    {
    case CDVD_READ_MMC:    ReadSector = Aspi_ReadSector_mmc;    break;
    case CDVD_READ_SCSI10: TRACE("cdvdlib: setting scsi10 readmode.\n");
        ReadSector = Aspi_ReadSector_scsi10; 
        if(((this)->*(SetSectorSize))((m_nMmcDataMode == CDVD_MMC_DATAMODE_RAW) ? 
            CDVD_SECTOR_SIZE_CD : CDVD_SECTOR_SIZE_DVD) != CDVD_ERROR_SUCCESS)
        {
            retval = CDVD_ERROR_FAIL;
        }
        break;
    case CDVD_READ_D8:     ReadSector = Aspi_ReadSector_matsu;  break;      
    case CDVD_READ_D410:   ReadSector = Aspi_ReadSector_sony;   break;
    case CDVD_READ_D412:   ReadSector = Aspi_ReadSector_nec;    break;
    default: retval = CDVD_ERROR_FAIL; break;
    }

    UpdateInterfaceObject();

    return retval;
}

// search for scsi adapters with c/dvd drives.
int CCdvd::Aspi_ScsiBusScan()
{
    int retval = CDVD_ERROR_SUCCESS;

    SRB_HAInquiry srbHAInquiry;
    m_nDrives = 0;
    memset(m_drvDetails, 0, CDVD_MAX_SUPPORTED_DRIVES * sizeof(ADAPTERINFO));
    memset(&srbHAInquiry, 0, sizeof (SRB_HAInquiry) );

    srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
    srbHAInquiry.SRB_HaId = 0;

    SendASPI32Command ((LPSRB) &srbHAInquiry);
    if (srbHAInquiry.SRB_Status != SS_COMP)
    {
         return CDVD_ERROR_FAIL;
    }

    UI08 HA_Count = srbHAInquiry.HA_Count;
    srbHAInquiry.HA_ManagerId[16] = 0;

    for (UI08 HA_num = 0; HA_num < HA_Count; HA_num++)
    {
        char szHA_num[10];
        itoa((int) HA_num, szHA_num, 10);
        memset(&srbHAInquiry, 0, sizeof(SRB_HAInquiry));
        srbHAInquiry.SRB_Cmd  = SC_HA_INQUIRY;
        srbHAInquiry.SRB_HaId = HA_num;

        SendASPI32Command ((LPSRB) &srbHAInquiry);
        if (srbHAInquiry.SRB_Status != SS_COMP)
        {
            return CDVD_ERROR_FAIL;
        }
        else
        {
            srbHAInquiry.HA_Identifier[16] = 0;
            for (UI08 SCSI_Id = 0; SCSI_Id < 8; SCSI_Id++)
            {
                char szSCSI_Id[10];
                itoa ((int) SCSI_Id, szSCSI_Id, 10);
                for(UI08 SCSI_Lun = 0; SCSI_Lun < 8; SCSI_Lun++)
                {
                    char szSCSI_Lun[10];
                    itoa ((int) SCSI_Lun, szSCSI_Lun, 10);

                    SRB_GDEVBlock srbGDEVBlock;
                    memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
                    srbGDEVBlock.SRB_Cmd    = SC_GET_DEV_TYPE;
                    srbGDEVBlock.SRB_HaId   = HA_num;
                    srbGDEVBlock.SRB_Target = SCSI_Id;
                    srbGDEVBlock.SRB_Lun    = SCSI_Lun;
                    SendASPI32Command ((LPSRB) &srbGDEVBlock);
                    if (srbGDEVBlock.SRB_Status != SS_COMP || 
                        srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM) continue;

                    char temp[40];
                    if(Aspi_ScsiInquiry(HA_num, SCSI_Id, SCSI_Lun, temp) == CDVD_ERROR_SUCCESS)
                    {
                        if(m_nDrives < CDVD_MAX_SUPPORTED_DRIVES)
                        {
                            TRACE("drive %ld: [%ld:%ld:%ld] - %s %s\n", m_nDrives, HA_num, SCSI_Id, SCSI_Lun, 
                                m_drvDetails[m_nDrives].hostname,
                                temp);
                            m_drvDetails[m_nDrives].ha  = HA_num;
                            m_drvDetails[m_nDrives].id  = SCSI_Id;
                            m_drvDetails[m_nDrives].lun = SCSI_Lun;
                            strcpy(m_drvDetails[m_nDrives].hostname, (const char*) srbHAInquiry.HA_Identifier);
                            strcpy(m_drvDetails[m_nDrives].name, temp);
                            ++m_nDrives;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        retval = CDVD_ERROR_FAIL;
                        break;
                    }
                }
            }
        }
    }

    return retval;
}

// retrieve ha info
int CCdvd::Aspi_ScsiInquiry(UI08 ha, UI08 id, UI08 lun, char *destination)
{
    int retval = CDVD_ERROR_SUCCESS;

	UI08 buffer[36];
  	SRB_ExecSCSICmd srb;

	memset(buffer, 0x20, sizeof(buffer));
	memset(&srb, 0, sizeof(SRB_ExecSCSICmd));

	srb.SRB_Cmd			= SC_EXEC_SCSI_CMD;
	srb.SRB_HaId		= ha;
	srb.SRB_Flags		= SRB_DIR_IN;
	srb.SRB_Target		= id;
	srb.SRB_Lun			= lun;
	srb.SRB_BufLen		= 36;
	srb.SRB_BufPointer	= buffer;
	srb.SRB_SenseLen	= SENSE_LEN;
	srb.SRB_CDBLen		= 6;
	srb.CDBByte[0]	    = SCSI_INQUIRY;
	srb.CDBByte[4]	    = 36;  // allocation length per buffer

	if (Aspi_ExecuteSrb(srb, NULL, 0) == CDVD_ERROR_SUCCESS)
	{
        memcpy(destination, buffer + 8, 27);
        destination[27] = '\0';
	}
	else
	{
		destination[0] = '\0';
	}

    return retval;
}

/***********************************************************/
/*              ASPI Execute SRB                           */
/***********************************************************/
// if HANDLE = NULL, SRB is synchronous, else its asynchronous
int CCdvd::Aspi_ExecuteSrb(SRB_ExecSCSICmd &srbExec, HANDLE *handle, int nretry)
{
    int retval = CDVD_ERROR_SUCCESS;

    ++nretry;    
    // lets do sync with handle == NULL
    if(handle == NULL)
    {
        TRACE("executing synchronous SRB.\n");
        while(nretry)
	    {
	        HANDLE heventExec = CreateEvent( NULL, TRUE, FALSE, NULL );
            if(heventExec) // do event based notification
            {
                srbExec.SRB_Flags |= SRB_EVENT_NOTIFY;
                srbExec.SRB_PostProc = (LPVOID)heventExec;
        
                DWORD dwASPIStatus = SendASPI32Command((LPSRB) &srbExec);
			    if(dwASPIStatus == SS_ERR) 
			    {
				    CloseHandle(heventExec);
				    return CDVD_ERROR_FAIL;
			    }

                if(dwASPIStatus == SS_PENDING)
                {
				    WaitForSingleObject(heventExec, INFINITE);
                }
                CloseHandle(heventExec);
            }
            else // do shitty polling
            {
                SendASPI32Command((LPSRB)&srbExec);
                while(srbExec.SRB_Status == SS_PENDING);
            }
            
            TRACE("SRB_Status: %ld\n", srbExec.SRB_Status);
	        if( srbExec.SRB_Status != SS_COMP )
            {
                if(nretry-- && 
                    (srbExec.SRB_TargStat != STATUS_CHKCOND ||
                    (srbExec.SenseArea[2] & 0x0F) != KEY_UNITATT) )
                {
			        UI08 sense = srbExec.SenseArea[2] & 0x0F;
			        UI08 asc   = srbExec.SenseArea[12];
			        UI08 ascq  = srbExec.SenseArea[13];
                    TRACE("sensekey: %02Xh, %02Xh, %02Xh\n", sense, asc, ascq); 
                    continue;
                }
            }
            else
            {
                // we're done.
                break;
            }
        }
    }
    else
    {
        TRACE("executing asynchronous SRB.\n");

        ResetEvent(*handle);
        srbExec.SRB_Flags   |= SRB_EVENT_NOTIFY;
        srbExec.SRB_PostProc = *handle;
    
        DWORD dwASPIStatus = SendASPI32Command((LPSRB) &srbExec);

	    if(dwASPIStatus == SS_ERR) 
	    {
		    SetEvent(*handle);
		    TRACE("SRB unrecoverable error.\n");
            return CDVD_ERROR_FAIL;
	    }
    
	     if(dwASPIStatus == SS_PENDING) 
         {
             TRACE("SRB Pending completion.\n");
		     return CDVD_ERROR_PENDING;
         }
        
         TRACE("SRB completed immediately.\n");       
    }

    if(nretry == 0) retval = CDVD_ERROR_FAIL;
	return retval;
}

/***********************************************************/
/*              ASPI C/DVD Methods                         */
/***********************************************************/

// read using mmc 0xbe command
int CCdvd::Aspi_ReadSector_mmc(int sect, HANDLE *handle)
{
	_ASSERT(m_bAspiInitialized == TRUE);

	m_srbReadCd[m_nCurrentBuffer].SRB_Cmd		 = SC_EXEC_SCSI_CMD;
	m_srbReadCd[m_nCurrentBuffer].SRB_HaId	     = m_drvDetails[m_nCurrentDrive].ha;
	m_srbReadCd[m_nCurrentBuffer].SRB_Flags	     = SRB_DIR_IN | SRB_ENABLE_RESIDUAL_COUNT;
	m_srbReadCd[m_nCurrentBuffer].SRB_Target	 = m_drvDetails[m_nCurrentDrive].id;
	m_srbReadCd[m_nCurrentBuffer].SRB_BufPointer = m_ReadBuffer[m_nCurrentBuffer].AB_BufPointer;
	m_srbReadCd[m_nCurrentBuffer].SRB_BufLen	 = m_ReadBuffer[m_nCurrentBuffer].AB_BufLen;
	m_srbReadCd[m_nCurrentBuffer].SRB_Lun		 = m_drvDetails[m_nCurrentDrive].lun;	
	m_srbReadCd[m_nCurrentBuffer].SRB_SenseLen   = SENSE_LEN;

	m_srbReadCd[m_nCurrentBuffer].SRB_CDBLen  = 12;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[0]  = 0xBE;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[1]  = 0x00;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[2]  = HIBYTE(HIWORD(sect));
	m_srbReadCd[m_nCurrentBuffer].CDBByte[3]  = LOBYTE(HIWORD(sect));
	m_srbReadCd[m_nCurrentBuffer].CDBByte[4]  = HIBYTE(LOWORD(sect));
	m_srbReadCd[m_nCurrentBuffer].CDBByte[5]  = LOBYTE(LOWORD(sect));
	m_srbReadCd[m_nCurrentBuffer].CDBByte[6]  = 0x00;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[7]  = 0x00;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[8]  = CDVD_NUM_SECTORS_PER_BUFF;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[9]  = (UI08) m_nMmcDataMode; 
	m_srbReadCd[m_nCurrentBuffer].CDBByte[10] = 0x00;
	m_srbReadCd[m_nCurrentBuffer].CDBByte[11] = 0x00;

    return Aspi_ExecuteSrb(m_srbReadCd[m_nCurrentBuffer], handle, CDVD_MAX_RETRY);
}

// read using scsi-10 (0x28) command, size is dependent on SetSectorSize
int CCdvd::Aspi_ReadSector_scsi10(int sect, HANDLE *handle)
{
	_ASSERT(m_bAspiInitialized == TRUE);

	m_srbRead10[m_nCurrentBuffer].SRB_Cmd		 = SC_EXEC_SCSI_CMD;
	m_srbRead10[m_nCurrentBuffer].SRB_HaId	     = m_drvDetails[m_nCurrentDrive].ha;
	m_srbRead10[m_nCurrentBuffer].SRB_Flags	     = SRB_DIR_IN;// | SRB_ENABLE_RESIDUAL_COUNT;
	m_srbRead10[m_nCurrentBuffer].SRB_Target	 = m_drvDetails[m_nCurrentDrive].id;
	m_srbRead10[m_nCurrentBuffer].SRB_BufPointer = m_ReadBuffer[m_nCurrentBuffer].AB_BufPointer;
	m_srbRead10[m_nCurrentBuffer].SRB_BufLen	 = m_ReadBuffer[m_nCurrentBuffer].AB_BufLen;
	m_srbRead10[m_nCurrentBuffer].SRB_Lun		 = m_drvDetails[m_nCurrentDrive].lun;	
	m_srbRead10[m_nCurrentBuffer].SRB_SenseLen   = SENSE_LEN;

	m_srbRead10[m_nCurrentBuffer].SRB_CDBLen  = 10;
	m_srbRead10[m_nCurrentBuffer].CDBByte[0]  = 0x28;
	m_srbRead10[m_nCurrentBuffer].CDBByte[1]  = 0x00;
	m_srbRead10[m_nCurrentBuffer].CDBByte[2]  = HIBYTE(HIWORD(sect));
	m_srbRead10[m_nCurrentBuffer].CDBByte[3]  = LOBYTE(HIWORD(sect));
	m_srbRead10[m_nCurrentBuffer].CDBByte[4]  = HIBYTE(LOWORD(sect));
	m_srbRead10[m_nCurrentBuffer].CDBByte[5]  = LOBYTE(LOWORD(sect));
	m_srbRead10[m_nCurrentBuffer].CDBByte[6]  = 0x00;
	m_srbRead10[m_nCurrentBuffer].CDBByte[7]  = 0x00;
	m_srbRead10[m_nCurrentBuffer].CDBByte[8]  = CDVD_NUM_SECTORS_PER_BUFF;
	m_srbRead10[m_nCurrentBuffer].CDBByte[9]  = 0x00; 

    return Aspi_ExecuteSrb(m_srbRead10[m_nCurrentBuffer], handle, CDVD_MAX_RETRY);
}

// read using proprietary matsushita command, heck if it works
int CCdvd::Aspi_ReadSector_matsu(int sect, HANDLE *handle)
{
	_ASSERT(m_bAspiInitialized == TRUE);

	m_srbReadMat[m_nCurrentBuffer].SRB_Cmd	      = SC_EXEC_SCSI_CMD;
	m_srbReadMat[m_nCurrentBuffer].SRB_HaId	      = m_drvDetails[m_nCurrentDrive].ha;
	m_srbReadMat[m_nCurrentBuffer].SRB_Flags	  = SRB_DIR_IN | SRB_ENABLE_RESIDUAL_COUNT;
	m_srbReadMat[m_nCurrentBuffer].SRB_Target	  = m_drvDetails[m_nCurrentDrive].id;
	m_srbReadMat[m_nCurrentBuffer].SRB_BufPointer = m_ReadBuffer[m_nCurrentBuffer].AB_BufPointer;
	m_srbReadMat[m_nCurrentBuffer].SRB_BufLen	  = m_ReadBuffer[m_nCurrentBuffer].AB_BufLen;
	m_srbReadMat[m_nCurrentBuffer].SRB_Lun		  = m_drvDetails[m_nCurrentDrive].lun;	
	m_srbReadMat[m_nCurrentBuffer].SRB_SenseLen   = SENSE_LEN;

	m_srbReadMat[m_nCurrentBuffer].SRB_CDBLen  = 12;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[0]  = 0xd4;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[1]  = 0x00;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[2]  = HIBYTE(HIWORD(sect));
	m_srbReadMat[m_nCurrentBuffer].CDBByte[3]  = LOBYTE(HIWORD(sect));
	m_srbReadMat[m_nCurrentBuffer].CDBByte[4]  = HIBYTE(LOWORD(sect));
	m_srbReadMat[m_nCurrentBuffer].CDBByte[5]  = LOBYTE(LOWORD(sect));
	m_srbReadMat[m_nCurrentBuffer].CDBByte[6]  = 0x00;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[7]  = 0x00;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[8]  = CDVD_NUM_SECTORS_PER_BUFF;
	m_srbReadMat[m_nCurrentBuffer].CDBByte[9]  = 0x00; 
	m_srbReadMat[m_nCurrentBuffer].CDBByte[10] = 0x00; 
	m_srbReadMat[m_nCurrentBuffer].CDBByte[11] = 0x00; 

    return Aspi_ExecuteSrb(m_srbReadMat[m_nCurrentBuffer], handle, CDVD_MAX_RETRY);
}

// read using proprietary sony command, heck if it works
int CCdvd::Aspi_ReadSector_sony(int sect, HANDLE *handle)
{
	_ASSERT(m_bAspiInitialized == TRUE);

	m_srbReadSony[m_nCurrentBuffer].SRB_Cmd	       = SC_EXEC_SCSI_CMD;
	m_srbReadSony[m_nCurrentBuffer].SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
	m_srbReadSony[m_nCurrentBuffer].SRB_Flags	   = SRB_DIR_IN | SRB_ENABLE_RESIDUAL_COUNT;
	m_srbReadSony[m_nCurrentBuffer].SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
	m_srbReadSony[m_nCurrentBuffer].SRB_BufPointer = m_ReadBuffer[m_nCurrentBuffer].AB_BufPointer;
	m_srbReadSony[m_nCurrentBuffer].SRB_BufLen	   = m_ReadBuffer[m_nCurrentBuffer].AB_BufLen;
	m_srbReadSony[m_nCurrentBuffer].SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;	
	m_srbReadSony[m_nCurrentBuffer].SRB_SenseLen   = SENSE_LEN;

	m_srbReadSony[m_nCurrentBuffer].SRB_CDBLen  = 12;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[0]  = 0xd8;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[1]  = 0x00;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[2]  = HIBYTE(HIWORD(sect));
	m_srbReadSony[m_nCurrentBuffer].CDBByte[3]  = LOBYTE(HIWORD(sect));
	m_srbReadSony[m_nCurrentBuffer].CDBByte[4]  = HIBYTE(LOWORD(sect));
	m_srbReadSony[m_nCurrentBuffer].CDBByte[5]  = LOBYTE(LOWORD(sect));
	m_srbReadSony[m_nCurrentBuffer].CDBByte[6]  = 0x00;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[7]  = 0x00;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[8]  = CDVD_NUM_SECTORS_PER_BUFF;
	m_srbReadSony[m_nCurrentBuffer].CDBByte[9]  = 0x00; 
	m_srbReadSony[m_nCurrentBuffer].CDBByte[10] = 0x00; 
	m_srbReadSony[m_nCurrentBuffer].CDBByte[11] = 0x00; 

    return Aspi_ExecuteSrb(m_srbReadSony[m_nCurrentBuffer], handle, CDVD_MAX_RETRY);
}

// read using proprietary nec command, heck if it works
int CCdvd::Aspi_ReadSector_nec(int sect, HANDLE *handle)
{
	_ASSERT(m_bAspiInitialized == TRUE);

	m_srbReadNec[m_nCurrentBuffer].SRB_Cmd	      = SC_EXEC_SCSI_CMD;
	m_srbReadNec[m_nCurrentBuffer].SRB_HaId	      = m_drvDetails[m_nCurrentDrive].ha;
	m_srbReadNec[m_nCurrentBuffer].SRB_Flags	  = SRB_DIR_IN | SRB_ENABLE_RESIDUAL_COUNT;
	m_srbReadNec[m_nCurrentBuffer].SRB_Target	  = m_drvDetails[m_nCurrentDrive].id;
	m_srbReadNec[m_nCurrentBuffer].SRB_BufPointer = m_ReadBuffer[m_nCurrentBuffer].AB_BufPointer;
	m_srbReadNec[m_nCurrentBuffer].SRB_BufLen	  = m_ReadBuffer[m_nCurrentBuffer].AB_BufLen;
	m_srbReadNec[m_nCurrentBuffer].SRB_Lun		  = m_drvDetails[m_nCurrentDrive].lun;	
	m_srbReadNec[m_nCurrentBuffer].SRB_SenseLen   = SENSE_LEN;

	m_srbReadNec[m_nCurrentBuffer].SRB_CDBLen  = 10;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[0]  = 0xd4;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[1]  = 0x00;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[2]  = HIBYTE(HIWORD(sect));
	m_srbReadNec[m_nCurrentBuffer].CDBByte[3]  = LOBYTE(HIWORD(sect));
	m_srbReadNec[m_nCurrentBuffer].CDBByte[4]  = HIBYTE(LOWORD(sect));
	m_srbReadNec[m_nCurrentBuffer].CDBByte[5]  = LOBYTE(LOWORD(sect));
	m_srbReadNec[m_nCurrentBuffer].CDBByte[6]  = 0x00;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[7]  = 0x00;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[8]  = CDVD_NUM_SECTORS_PER_BUFF;
	m_srbReadNec[m_nCurrentBuffer].CDBByte[9]  = 0x00; 

    return Aspi_ExecuteSrb(m_srbReadNec[m_nCurrentBuffer], handle, CDVD_MAX_RETRY);
}

// retrieve number of sectors present in c/dvd media 
int CCdvd::Aspi_GetNumSectors()
{
	_ASSERT(m_bAspiInitialized == TRUE);
    
    SRB_ExecSCSICmd srb;
    UI08 capacity[8];
    UI32 max_sector;

    memset(&srb, 0, sizeof(SRB_ExecSCSICmd));

    srb.SRB_Cmd			= SC_EXEC_SCSI_CMD;
    srb.SRB_HaId		= m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Flags		= SRB_DIR_IN;
    srb.SRB_Target		= m_drvDetails[m_nCurrentDrive].id;
	srb.SRB_Lun			= m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_BufLen		= 8;
    srb.SRB_BufPointer	= capacity;
    srb.SRB_SenseLen	= SENSE_LEN;
    srb.SRB_CDBLen		= 10;

    srb.CDBByte[0]      = SCSI_RD_CAPAC;     


    if(Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY) 
        != CDVD_ERROR_SUCCESS)
    	return 0; // no sectors found
 
    MOVESCSIDWORD(&capacity[0], &max_sector);

	return max_sector+1;
}

// retrieve TOC data
int CCdvd::Aspi_GetToc()
{   
	_ASSERT(m_bAspiInitialized == TRUE);

    SRB_ExecSCSICmd srb;
    UI08 buff[804];
    memset(buff, 0, sizeof(buff));
    memset (&srb, 0, sizeof(SRB_ExecSCSICmd));

    srb.SRB_Cmd		   = SC_EXEC_SCSI_CMD;
    srb.SRB_Flags      = SRB_DIR_IN;
    srb.SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_SenseLen   = SENSE_LEN;
    srb.SRB_BufPointer = buff;
    srb.SRB_BufLen     = sizeof(buff);
    srb.SRB_CDBLen	   = 10;
    srb.CDBByte[0]	   = SCSI_READ_TOC;
    srb.CDBByte[6]     = 0;
    srb.CDBByte[7]     = 0x03;
    srb.CDBByte[8]     = 0x24;

    if(Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY) 
        != CDVD_ERROR_SUCCESS)
        return CDVD_ERROR_FAIL;
    
    if(ExtractTocData(buff) != CDVD_ERROR_SUCCESS)
        return CDVD_ERROR_FAIL;
   
    return CDVD_ERROR_SUCCESS;
}

// set cdrom speed
int CCdvd::Aspi_SetSpeed(int speed)
{
	_ASSERT(m_bAspiInitialized == TRUE);
	
    SRB_ExecSCSICmd srb;     
	UI16 kbSpeed = 0xFFFF;
	switch(speed)
	{
	case 0: kbSpeed = 0xFFFF; break;
	case 1: kbSpeed = 176; break;
	case 2: kbSpeed = 353; break;  
	case 3: kbSpeed = 528; break;
	case 4: kbSpeed = 706; break;
	case 5: kbSpeed = 1400; break;
	case 6: kbSpeed = 2800; break;
	default: kbSpeed = 0xFFFF; break; // max
	}

    memset(&srb,0,sizeof(srb));

	srb.SRB_Cmd		 = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	 = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	 = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		 = m_drvDetails[m_nCurrentDrive].lun;
	srb.SRB_Flags	 = SRB_DIR_OUT; //|SRB_ENABLE_RESIDUAL_COUNT;
	srb.SRB_SenseLen = SENSE_LEN;

	srb.SRB_CDBLen   = 12;
	srb.CDBByte[0]   = 0xBB;
	srb.CDBByte[2]   = HIBYTE(kbSpeed);
	srb.CDBByte[3]   = LOBYTE(kbSpeed);

	return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);    
}

// used for scsi only, sets sector size (i.e, 2352, 2048)
int CCdvd::Aspi_SetSectorSize(int sect_size)
{
	_ASSERT(m_bAspiInitialized == TRUE);

    TRACE("sectorsize: %ld\n", sect_size);
    MODESELHEADER mh;
    SRB_ExecSCSICmd srb;
 
    memset (&srb, 0, sizeof(SRB_ExecSCSICmd));
    memset (&mh,  0, sizeof(MODESELHEADER));

    mh.block_desc_length	= 0x08;
    mh.block_length_med		= HIBYTE(LOWORD(sect_size));
    mh.block_length_lo		= LOBYTE(LOWORD(sect_size));

    srb.SRB_Cmd		   = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_SenseLen   = SENSE_LEN;
    srb.SRB_Flags	   = SRB_DIR_OUT;
    srb.SRB_BufLen	   = sizeof(mh);
    srb.SRB_BufPointer = (UI08 *) &mh;
    srb.SRB_CDBLen	   = 6;
    srb.CDBByte[0]	   = 0x15;
    srb.CDBByte[1]     = 0x10;
    srb.CDBByte[4]     = 0x0C;

    return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);
}

// start cddda playback, dunno if it works right really.. :p
int CCdvd::Aspi_Play(int sect_start, int sect_stop)
{
	_ASSERT(m_bAspiInitialized == TRUE);

    SRB_ExecSCSICmd srb;
	
    memset( &srb, 0, sizeof(SRB_ExecSCSICmd) );

    srb.SRB_Cmd		   = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_Flags	   = SRB_DIR_IN;
    srb.SRB_SenseLen   = SENSE_LEN;
    srb.SRB_CDBLen	   = 12;

	srb.CDBByte[0]  = 0xa5;
	srb.CDBByte[2]  = HIBYTE(HIWORD(sect_start));
	srb.CDBByte[3]  = LOBYTE(HIWORD(sect_start));
	srb.CDBByte[4]  = HIBYTE(LOWORD(sect_start));
	srb.CDBByte[5]  = LOBYTE(LOWORD(sect_start));
	srb.CDBByte[6]  = HIBYTE(HIWORD(sect_stop));
	srb.CDBByte[7]  = LOBYTE(HIWORD(sect_stop));
	srb.CDBByte[8]  = HIBYTE(LOWORD(sect_stop));
	srb.CDBByte[9]  = LOBYTE(LOWORD(sect_stop));
//	srb.CDBByte[10] = 0x8F;

    return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);
}

// stop cdda playback, dunno if it works right really.. :p
int CCdvd::Aspi_Stop()
{
	_ASSERT(m_bAspiInitialized == TRUE);

    SRB_ExecSCSICmd srb;
	
    memset(&srb, 0, sizeof(SRB_ExecSCSICmd));

    srb.SRB_Cmd		   = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_Flags	   = SRB_DIR_IN;
    srb.SRB_SenseLen   = SENSE_LEN;
    srb.SRB_CDBLen	   = 10;
	srb.CDBByte[0]	   = 0x4E;

    return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);
}

// detect if media/LUN is ready, legacy command, 
// can prolly be substituted with GetMediaType(), with some modifs
int CCdvd::Aspi_TestReady()
{
    SRB_ExecSCSICmd srb;

    memset (&srb, 0, sizeof(SRB_ExecSCSICmd));
    srb.SRB_Cmd		 = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	 = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	 = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		 = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_SenseLen = SENSE_LEN;
    srb.SRB_CDBLen	 = 6;
    srb.CDBByte[0]	 = SCSI_TST_U_RDY;
    
   return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);
}

// retrieve feature details
int CCdvd::Aspi_GetConfiguration(UI16 feature, UI08 *dest, UI32 dest_size)
{
    _ASSERT(m_bAspiInitialized == TRUE);

    int retval = CDVD_ERROR_SUCCESS;

    SRB_ExecSCSICmd srb;
    memset(&srb,   0, sizeof(SRB_ExecSCSICmd));
    memset(dest,   0, dest_size);

    srb.SRB_Cmd		   = SC_EXEC_SCSI_CMD;
    srb.SRB_HaId	   = m_drvDetails[m_nCurrentDrive].ha;
    srb.SRB_Target	   = m_drvDetails[m_nCurrentDrive].id;
    srb.SRB_Lun		   = m_drvDetails[m_nCurrentDrive].lun;
    srb.SRB_Flags	   = SRB_DIR_IN;
    srb.SRB_SenseLen   = SENSE_LEN;
    srb.SRB_BufPointer = dest;
    srb.SRB_BufLen     = dest_size;

	srb.SRB_CDBLen  = 12;
	srb.CDBByte[0]  = 0x46;
	srb.CDBByte[1]  = 0x02; // return a single feature set ONLY.
	srb.CDBByte[2]  = HIBYTE(feature);
	srb.CDBByte[3]  = LOBYTE(feature);
	srb.CDBByte[7]  = HIBYTE(dest_size);
	srb.CDBByte[8]  = LOBYTE(dest_size);

    return Aspi_ExecuteSrb(srb, NULL, CDVD_MAX_RETRY);
}
