scsi2.c

来自「嵌入式操作系统WINCE5.0下的USB驱动程序」· C语言 代码 · 共 1,496 行 · 第 1/4 页

C
1,496
字号
    #define MAX_LIST_LENGTH     512             // Note: USB Mass Storage Spec says this is max 72 bytes 
    UCHAR   bDataBlock[MAX_LIST_LENGTH] = {0};  // Standard Header + mode pages
    USHORT  usPageLength = 8;                   // Standard Header size

    DWORD dwErr = ERROR_SUCCESS;

    DEBUGMSG(ZONE_SCSI,(TEXT("USBDISK6>ScsiModeSense\n")));

    dwErr = AcquireRemoveLock(&pDevice->RemoveLock, NULL) ;
    if ( ERROR_SUCCESS != dwErr) {
        return dwErr;
    }

    ASSERT( SCSI_DEVICE_DIRECT_ACCESS == pDevice->DeviceType || 
            SCSI_DEVICE_CDROM == pDevice->DeviceType );

    memset( bCDB, 0, sizeof(bCDB));

    tCommand.Flags   = DATA_IN;
    tCommand.Timeout = pDevice->Timeouts.ScsiCommandTimeout;
    tCommand.Length  = USBMSC_SUBCLASS_SCSI == pDevice->DiskSubClass ?
						SCSI_CDB_10 : UFI_CDB;
    tCommand.CommandBlock = bCDB;
    tCommand.dwLun=Lun;

    bCDB[0] = SCSI_MODE_SENSE10;

    ASSERT(Lun <= 0x7);
    bCDB[1] = ((Lun & 0x7) << 5);
    // bCDB[1] |= 0x8;      // DBD  Note: USB Mass Storage Spec says this is 0

    // PC = current
    // Page Code = ALL
    bCDB[2] = 0x3f;			// this was original set to 0 but 0 is not defined in the USB Mass Storage Spec 
							//		some devices return ILLEGAL REQUEST. So use some defines value as default

    switch (pDevice->DeviceType) 
    {
        case SCSI_DEVICE_DIRECT_ACCESS:
            if (pDevice->DiskSubClass == USBMSC_SUBCLASS_UFI) {
                bCDB[2] = MODE_PAGE_FLEXIBLE_DISK;
                usPageLength += 32;
            } else {
                usPageLength = sizeof(bDataBlock);
            }
            break;

        case SCSI_DEVICE_CDROM:
            bCDB[2] = MODE_PAGE_CDROM;
            usPageLength += 8;
            break;

        default:
            DEBUGMSG(ZONE_ERR,(TEXT("ScsiModeSense10: Unknown DeviceType:0x%x\n"),
                    pDevice->DeviceType));
            TEST_TRAP();
            usPageLength = sizeof(bDataBlock);
            break;
    }

    bCDB[7] = (sizeof(bDataBlock) & 0xFF00) >> 8; // MSB
    bCDB[8] =  sizeof(bDataBlock) & 0x00FF;       // LSB

    memset( bDataBlock, 0, sizeof(bDataBlock));
    ASSERT( usPageLength <= sizeof(bDataBlock));

    tData.TransferLength = 0;
    // Note: some devices fail with only 8 byte header request length, 
    // but all should recover with extra buffer space.
    tData.RequestLength = usPageLength;
    tData.DataBlock = bDataBlock;

    dwErr = UsbsDataTransfer( pDevice->hUsbTransport,
                             &tCommand,
                             &tData );

    if ( dwErr != ERROR_SUCCESS || tData.TransferLength < 8 ) { // want at least the header

        dwErr = ScsiGetSenseData( pDevice, Lun );
        
        SetLastError(dwErr);

        DEBUGMSG(ZONE_ERR,(TEXT("ScsiModeSense ERROR:%d\n"), dwErr));

    } else {
        
        EnterCriticalSection(&pDevice->Lock);

        // look at the Header
        DEBUGMSG(ZONE_SCSI,(TEXT("Medium Type:0x%x\n"), bDataBlock[2]));

        pDevice->MediumType = bDataBlock[2];

        // bit7 is WP bit
        DEBUGMSG(ZONE_SCSI,(TEXT("Device Specific:0x%x\n"), bDataBlock[3] ));
        pDevice->Flags.WriteProtect = bDataBlock[3] & 0x80;
        
        LeaveCriticalSection(&pDevice->Lock);
        
        //
        // TBD: look at the the requested page ...
        //
    }

    ReleaseRemoveLock(&pDevice->RemoveLock, NULL);
    
    DEBUGMSG(ZONE_SCSI,(TEXT("USBDISK6<ScsiModeSense:%d\n"), dwErr));
    
    return dwErr;
}

DWORD
ScsiModeSense6(
    PSCSI_DEVICE pDevice,
    UCHAR        Lun
    )
{
    TRANSPORT_DATA    tData;
    TRANSPORT_COMMAND tCommand;
    UCHAR             bCDB[SCSI_CDB_6];
    UCHAR             bDataBlock[MAX_LIST_LENGTH] = {0}; // standard header + mode pages
    USHORT            usPageLength = 8;                  // standard header size
    DWORD             dwErr = ERROR_SUCCESS;

    dwErr = AcquireRemoveLock(&pDevice->RemoveLock, NULL);
    if (ERROR_SUCCESS != dwErr) {
        return dwErr;
    }

    ASSERT(SCSI_DEVICE_DIRECT_ACCESS == pDevice->DeviceType || SCSI_DEVICE_CDROM == pDevice->DeviceType);

    memset(bCDB, 0, sizeof(bCDB));
    tCommand.Flags = DATA_IN;
    tCommand.Timeout = pDevice->Timeouts.ScsiCommandTimeout;
    tCommand.Length = USBMSC_SUBCLASS_SCSI == pDevice->DiskSubClass ? SCSI_CDB_6 : UFI_CDB;
    tCommand.CommandBlock = bCDB;
    tCommand.dwLun = Lun;
    DEBUGCHK(Lun <= 0x7);

    bCDB[0] = SCSI_MODE_SENSE6;
    bCDB[1] = ((Lun & 0x7) << 5);
    bCDB[2] = 0x3f;

    switch (pDevice->DeviceType) {
        case SCSI_DEVICE_DIRECT_ACCESS:
            if (pDevice->DiskSubClass == USBMSC_SUBCLASS_UFI) {
                bCDB[2] = MODE_PAGE_FLEXIBLE_DISK;
                usPageLength += 32;
            }
            else {
                usPageLength = sizeof(bDataBlock);
            }
            break;
        case SCSI_DEVICE_CDROM:
            bCDB[2] = MODE_PAGE_CDROM;
            usPageLength += 8;
            break;
        default:
            usPageLength = sizeof(bDataBlock);
            break;
    }
    usPageLength = 8;
    DEBUGCHK(usPageLength <= sizeof(bDataBlock));

    memset(bDataBlock, 0, sizeof(bDataBlock));

    // a device may fail this command if the header request length is only 8 bytes;
    // if this is the case, then the device should recover with extra buffer space

    tData.TransferLength = 0;
    tData.RequestLength = usPageLength;
    tData.DataBlock = bDataBlock;

    dwErr = UsbsDataTransfer(pDevice->hUsbTransport, &tCommand, &tData);

    if (dwErr != ERROR_SUCCESS || tData.TransferLength < 8 ) { // we want at least the header
        dwErr = ScsiGetSenseData(pDevice, Lun);
        SetLastError(dwErr);
        DEBUGMSG(ZONE_ERR,(TEXT("ScsiModeSense6: device failed command\r\n"), dwErr));
    }
    else {
        EnterCriticalSection(&pDevice->Lock);
        DEBUGMSG(ZONE_SCSI,(TEXT("Scsi2ModeSense6: medium type=0x%x\n"), bDataBlock[2]));
        pDevice->MediumType = bDataBlock[2];
        pDevice->Flags.WriteProtect = bDataBlock[3] & 0x80; // inspect WP bit
        LeaveCriticalSection(&pDevice->Lock);
    }

    ReleaseRemoveLock(&pDevice->RemoveLock, NULL);
    return dwErr;
}

DWORD
ScsiStartStopUnit(
    PSCSI_DEVICE    pDevice,
    BOOL            Start,  // TRUE = START, else STOP
    BOOL            LoEj,
    UCHAR           Lun
    )
{
    TRANSPORT_COMMAND tCommand;
    UCHAR             bCDB[MAX_CDB];
    
    DWORD dwErr;
    
    DEBUGMSG(ZONE_SCSI,(TEXT("USBDISK6>ScsiStartStopUnit\n")));

    dwErr = AcquireRemoveLock(&pDevice->RemoveLock, NULL);
    if ( ERROR_SUCCESS != dwErr) {
        return dwErr;
    }

    tCommand.Flags   = DATA_OUT;
    tCommand.Timeout = pDevice->Timeouts.ScsiCommandTimeout;
    tCommand.Length  = USBMSC_SUBCLASS_SCSI == pDevice->DiskSubClass ?
                       SCSI_CDB_6 : UFI_CDB;
    tCommand.CommandBlock = bCDB;
    tCommand.dwLun=Lun;

    memset( bCDB, 0, sizeof(bCDB));
    bCDB[0] = SCSI_START_STOP;

    ASSERT(Lun <= 0x7);
    bCDB[1] = ((Lun & 0x7) << 5);

    bCDB[4] = (LoEj & 0x1) << 1;
    bCDB[4] |= Start & 0x1;

    dwErr = UsbsDataTransfer(pDevice->hUsbTransport,
                             &tCommand,
                             NULL );

    if ( dwErr != ERROR_SUCCESS ) {

        dwErr = ScsiGetSenseData( pDevice, Lun );

        DEBUGMSG(ZONE_ERR,(TEXT("ScsiStartStopUnit ERROR:%d\n"), dwErr));

        SetLastError(dwErr);

    }

    ReleaseRemoveLock(&pDevice->RemoveLock, NULL);
    
    DEBUGMSG(ZONE_SCSI,(TEXT("USBDISK6<ScsiStartStopUnit:%d\n"), dwErr));

    return dwErr;
}


//
// Checks the users SG to see if we can use it.
// Since we basically do a prescan then return flag saying
// if we need to use double buffering.
//
DWORD
CheckSegments(
    IN PSCSI_DEVICE    pDevice,
    IN PSG_REQ         pSgReq,
    IN OUT PDWORD      pTransferLength,
    IN OUT PDWORD      pFlags
    )
{
    DWORD dwErr = ERROR_SUCCESS;
    DWORD sg;

    DEBUGMSG(ZONE_TRACE,(TEXT("USBMSC>CheckSegments\n")));


    if ( !pDevice || 0 == pDevice->DiskInfo.di_bytes_per_sect || 
         0 == pDevice->DiskInfo.di_total_sectors ) 
    {
        dwErr = ERROR_FLOPPY_UNKNOWN_ERROR;
        DEBUGMSG(ZONE_ERR,(TEXT("CheckSegments ERROR:1: di_bytes_per_sect:%d di_total_sectors:%d\n"),
            pDevice?pDevice->DiskInfo.di_bytes_per_sect:-1, pDevice?pDevice->DiskInfo.di_total_sectors:-1));
        
        TEST_TRAP();

    } else if ( !pSgReq || !pTransferLength || !pFlags) {

        dwErr=ERROR_INVALID_PARAMETER;
        DEBUGMSG(ZONE_ERR,(TEXT("CheckSegments ERROR:2: %d\n"), dwErr ));
    
    } else if (pSgReq->sr_num_sg > MAX_SG_BUF ||
               pSgReq->sr_start + pSgReq->sr_num_sec > pDevice->DiskInfo.di_total_sectors)
    {
            dwErr=ERROR_INVALID_PARAMETER;
            DEBUGMSG(ZONE_ERR,(TEXT("CheckSegments ERROR:3: sg:%d sr_start:%d, sr_num_sec:%d, di_total_sectors:%d di_bytes_per_sect:%d\n"),
                pSgReq->sr_num_sg, pSgReq->sr_start, pSgReq->sr_num_sec, pDevice->DiskInfo.di_total_sectors, pDevice->DiskInfo.di_bytes_per_sect));
    
    } else {
        //
        // ensure all the buffers in the SG list are OK
        //
        ASSERT(pFlags);
        ASSERT(pTransferLength);
	    EnterCriticalSection(&pDevice->Lock);

        for (sg = 0, *pTransferLength = 0, *pFlags = 0; sg < pSgReq->sr_num_sg; sg++) 
        {
            if ( !pSgReq->sr_sglist[sg].sb_buf )
            {
                dwErr = ERROR_INVALID_PARAMETER;
                DEBUGMSG(ZONE_ERR,(TEXT("CheckSegments ERROR:4:%d\n"), dwErr));
                break;
            }

            // do we need double buffering?
            if ((pSgReq->sr_sglist[sg].sb_len % pDevice->DiskInfo.di_bytes_per_sect) != 0)
            {
                *pFlags |= 0x1;
                DEBUGMSG(ZONE_READ,(TEXT("CheckSegments: Double Buffered\n")));
            }

            // sum all the buffer lengths to make sure they match 
            // the requested number of sectors
            *pTransferLength += pSgReq->sr_sglist[sg].sb_len;
        }

        if (dwErr == ERROR_SUCCESS && 
            *pTransferLength > (pSgReq->sr_num_sec * pDevice->DiskInfo.di_bytes_per_sect)) 
        {
            dwErr = ERROR_INVALID_PARAMETER;
            DEBUGMSG(ZONE_ERR,(TEXT("CheckSegments ERROR:5: invalid user SG buffers (%u > %u)\n"),
            *pTransferLength, (pSgReq->sr_num_sg * pDevice->DiskInfo.di_bytes_per_sect)));
        }
               
	    LeaveCriticalSection(&pDevice->Lock);
    }

    DEBUGMSG(ZONE_TRACE,(TEXT("USBMSC<CheckSegments:%d\n"), dwErr));

    return dwErr;
}

/*++

    Validate a Scatter/Gather request object.

    Returns: If fail, Win32 error.
             If pass, ERROR_SUCCESS.
--*/
static DWORD
InspectSgReq(
    PSG_REQ pSgReq,
    UINT uiBytesPerSector
    )
{
    UINT uiSgIndex;         // Index into Scatter/Gather buffer array.
	UINT uiBytesToTransfer; // Running count of the number of bytes requested to transfer.
	
	DEBUGMSG(ZONE_TRACE, (TEXT("USBMSC>InspectSgReq\n")));

	if (pSgReq == NULL) {
		DEBUGMSG(ZONE_ERR, (TEXT("USBMSC>InspectSgReq: Error: pSqReq: NULL\n")));
		return ERROR_BAD_ARGUMENTS;
    }    
    if (pSgReq->sr_num_sec == 0) {
		DEBUGMSG(ZONE_ERR, (TEXT("USBMSC>InspectSgReq: Error: ->sr_num_sec: 0\n")));
		return ERROR_BAD_ARGUMENTS;
	}
	if (pSgReq->sr_num_sg == 0) {
		DEBUGMSG(ZONE_ERR, (TEXT("USBMSC>InspectSgReq: Error: ->sr_num_sg: 0\n")));
		return ERROR_BAD_ARGUMENTS;
	}
	DEBUGMSG(ZONE_TRACE, (TEXT("USBMSC>InspectSgReq: ->sr_num_sec: %d, ->sr_num_sg: %d, bytes/sec: %d\n"), pSgReq->sr_num_sec, pSgReq->sr_num_sg, uiBytesPerSector));
	for (uiSgIndex = 0, uiBytesToTransfer = 0; uiSgIndex < pSgReq->sr_num_sg; uiSgIndex += 1) {
		if (pSgReq->sr_sglist[uiSgIndex].sb_buf == NULL) {
			DEBUGMSG(ZONE_ERR, (TEXT("USBMSC>InspectSgReq: Error: ->sr_sglist[%d].sb_buf: NULL\n"), uiSgIndex));
			return ERROR_BAD_ARGUMENTS;
		}
		if (pSgReq->sr_sglist[uiSgIndex].sb_len == 0) {
			DEBUGMSG(ZONE_ERR, (TEXT("USBMSC>InspectSgReq: Error: ->sr_sglist[%d].sb_len: 0\n"), uiSgIndex));
			return ERROR_BAD_ARGUMENTS;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?