?? scsi.c
字號:
/******************************************************************
本程序只供學習使用,未經(jīng)作者許可,不得用于其它任何用途
歡迎訪問我的USB專區(qū):http://group.ednchina.com/93/
歡迎訪問我的blog: http://www.ednchina.com/blog/computer00
http://computer00.21ic.org
感謝PCB贊助商——電子園: http://bbs.cepark.com/
SCSI.c file
作者:電腦圈圈
建立日期: 2008.08.15
修改日期: 2008.08.20
版本:V1.1
版權所有,盜版必究。
Copyright(C) 電腦圈圈 2008-2018
All rights reserved
*******************************************************************/
#include "MyType.H"
#include "UsbCore.h"
#include "PDIUSBD12.h"
#include "SCSI.h"
#include "Uart.h"
#include "config.h"
#include "FAT.h"
//定義端點2最大包長度為64字節(jié)
#define EP2_SIZE 64
//處理端點2數(shù)據(jù)的緩沖區(qū)
idata uint8 Ep2Buffer[EP2_SIZE];
uint32 ByteAddr; //字節(jié)地址
//INQUIRY命令需要返回的數(shù)據(jù)
//請對照書中INQUIRY命令響應數(shù)據(jù)格式
code uint8 DiskInf[36]=
{
0x00, //磁盤設備
0x00, //其中最高位D7為RMB。RMB=0,表示不可移除設備。如果RMB=1,則為可移除設備。
0x00, //各種版本號0
0x01, //數(shù)據(jù)響應格式
0x1F, //附加數(shù)據(jù)長度,為31字節(jié)
0x00, //保留
0x00, //保留
0x00, //保留
0xB5,0xE7,0XC4,0xD4,0xC8,0xA6,0xC8,0xA6, //廠商標識,為字符串“電腦圈圈”
//產品標識,為字符串“自己做的假U盤”
0xD7,0xD4,0xBC,0xBA,0xD7,0xF6,0xB5,0xC4,0xBC,0xD9,0x55,0xC5,0xCC,0x00,0x00,0x00,
0x31,0x2E,0x30,0x31 //產品版本號,為1.01
};
//READ_FORMAT_CAPACITIES命令需要返回的數(shù)據(jù)
//請對照書中READ_FORMAT_CAPACITIES命令響應數(shù)據(jù)格式
code uint8 MaximumCapacity[12]=
{
0x00, 0x00, 0x00, //保留
0x08, //容量列表長度
0x01, 0x00, 0x00, 0x00, //塊數(shù)(最大支持8GB)
0x03, //描述符代碼為3,表示最大支持的格式化容量
0x00, 0x02, 0x00 //每塊大小為512字節(jié)
};
//READ_CAPACITY命令需要返回的數(shù)據(jù)
code uint8 DiskCapacity[8]=
{
0x00,0x03,0xFF,0xFF, //能夠訪問的最大邏輯塊地址
0x00,0x00,0x02,0x00 //塊的長度
//所以該磁盤的容量為
//(0x3FFFF+1)*0x200 = 0x8000000 = 128*1024*1024 = 128MB.
};
//REQUEST SENSE命令需要返回的數(shù)據(jù),這里固定為無效命令
//請參看書總數(shù)據(jù)結構的解釋
code uint8 SenseData[18]=
{
0x70, //錯誤代碼,固定為0x70
0x00, //保留
0x05, //Sense Key為0x05,表示無效請求(ILLEGAL REQUEST)
0x00, 0x00, 0x00, 0x00, //Information為0
0x0A, //附加數(shù)據(jù)長度為10字節(jié)
0x00, 0x00, 0x00, 0x00, //保留
0x20, //Additional Sense Code(ASC)為0x20,表示無效命令操作碼(INVALID COMMAND OPERATION CODE)
0x00, //Additional Sense Code Qualifier(ASCQ)為0
0x00, 0x00, 0x00, 0x00 //保留
};
uint8 * pEp2SendData;
uint32 Ep2DataLength;
/********************************************************************
函數(shù)功能:從CBW中獲取傳輸數(shù)據(jù)的字節(jié)數(shù)。
入口參數(shù):無。
返 回:需要傳輸?shù)淖止?jié)數(shù)。
備 注:無。
********************************************************************/
uint32 GetDataTransferLength(void)
{
uint32 Len;
//CBW[8]~CBW[11]為傳輸長度(小端結構)
Len=CBW[11];
Len=Len*256+CBW[10];
Len=Len*256+CBW[9];
Len=Len*256+CBW[8];
return Len;
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:從CBW中獲取邏輯塊地址LBA的字節(jié)數(shù)。
入口參數(shù):無。
返 回:邏輯塊地址LBA。
備 注:無。
********************************************************************/
uint32 GetLba(void)
{
uint32 Lba;
//讀和寫命令時,CBW[17]~CBW[20]為邏輯塊地址(大端結構)
Lba=CBW[17];
Lba=Lba*256+CBW[18];
Lba=Lba*256+CBW[19];
Lba=Lba*256+CBW[20];
return Lba;
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:填充CSW。
入口參數(shù):Residue:剩余字節(jié)數(shù);Status:命令執(zhí)行的狀態(tài)。
返 回:無。
備 注:無。
********************************************************************/
void SetCsw(uint32 Residue, uint8 Status)
{
//設置CSW的簽名,其實可以不用每次都設置的,
//開始初始化設置一次就行了,這里每次都設置
CSW[0]='U';
CSW[1]='S';
CSW[2]='B';
CSW[3]='S';
//復制dCBWTag到CSW的dCSWTag中去
CSW[4]=CBW[4];
CSW[5]=CBW[5];
CSW[6]=CBW[6];
CSW[7]=CBW[7];
//剩余字節(jié)數(shù)
CSW[8]=Residue&0xFF;
CSW[9]=(Residue>>8)&0xFF;
CSW[10]=(Residue>>16)&0xFF;
CSW[11]=(Residue>>24)&0xFF;
//命令執(zhí)行的狀態(tài),0表示成功,1表示失敗。
CSW[12]=Status;
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:獲取磁盤數(shù)據(jù)函數(shù)。
入口參數(shù):無。
返 回:無。
備 注:無。
********************************************************************/
void GetDiskData(void)
{
//判斷該返回什么數(shù)據(jù)
if(ByteAddr==0) pEp2SendData=Dbr; //返回DBR
if(ByteAddr==512) pEp2SendData=Fat; //返回FAT
if((ByteAddr>=576)&&(ByteAddr<16896)) pEp2SendData=Zeros;
if(ByteAddr==16896) pEp2SendData=Fat; //返回FAT(備份FAT)
if((ByteAddr>=16960)&&(ByteAddr<33280)) pEp2SendData=Zeros;
if(ByteAddr==33280) pEp2SendData=RootDir; //返回根目錄
if((ByteAddr>=33344)&&(ByteAddr<49664)) pEp2SendData=Zeros;
if(ByteAddr==49664) pEp2SendData=TestFileData; //返回文件數(shù)據(jù)
if(ByteAddr>50175) pEp2SendData=Zeros;
ByteAddr+=EP2_SIZE; //調整字節(jié)地址,每次發(fā)送最大包長度的數(shù)據(jù)
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:將數(shù)據(jù)通過端點2發(fā)送。
入口參數(shù):無。
返 回:無。
備 注:當發(fā)送數(shù)據(jù)長度為0,并且處于數(shù)據(jù)階段時,將自動發(fā)送CSW。
********************************************************************/
void Ep2SendData(void)
{
if(Ep2DataLength==0) //如果需要發(fā)送的數(shù)據(jù)長度為0
{
if(TransportStage==DATA_STAGE) //并且處于數(shù)據(jù)階段
{
//則直接進入狀態(tài)階段
TransportStage=STATUS_STAGE;
Ep2DataLength=sizeof(CSW); //數(shù)據(jù)長度為CSW的大小
pEp2SendData=CSW; //返回的數(shù)據(jù)為CSW
}
else
{
return; //如果是狀態(tài)階段的數(shù)據(jù)發(fā)送完畢,則返回
}
}
#ifdef DEBUG0
if(TransportStage==STATUS_STAGE)
{
Prints("狀態(tài)階段。\r\n");
}
#endif
//如果要發(fā)送的長度比端點2最大包長要多,則分多個包發(fā)送
if(Ep2DataLength>EP2_SIZE)
{
//發(fā)送端點2最大長度字節(jié)
D12WriteEndpointBuffer(5,EP2_SIZE,pEp2SendData);
//指針移動EP2_SIZE字節(jié)
pEp2SendData+=EP2_SIZE;
Ep2DataLength-=EP2_SIZE;
//如果是READ(10)命令,并且是數(shù)據(jù)階段,則需要獲取磁盤數(shù)據(jù)
if((CBW[15]==READ_10)&&(TransportStage==DATA_STAGE))
{
GetDiskData(); //獲取磁盤數(shù)據(jù)
}
}
else
{
//可以全部發(fā)送完
D12WriteEndpointBuffer(5,(uint8)Ep2DataLength,pEp2SendData);
Ep2DataLength=0; //傳輸長度為0
//如果是數(shù)據(jù)發(fā)送完畢,則進入僅批量傳輸協(xié)議的狀態(tài)階段
if(TransportStage==DATA_STAGE)
{
TransportStage=STATUS_STAGE;
Ep2DataLength=sizeof(CSW); //數(shù)據(jù)長度為CSW的大小
pEp2SendData=CSW; //返回的數(shù)據(jù)為CSW
}
else if(TransportStage==STATUS_STAGE) //如果是狀態(tài)階段完畢,則進入到命令階段
{
TransportStage=COMMAND_STAGE; //進入到命令階段
}
}
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:處理SCSI命令的函數(shù)。
入口參數(shù):無。
返 回:無。
備 注:雖然叫SCSI命令,但是實際使用的是UFI命令。
********************************************************************/
void ProcScsiCommand(void)
{
TransportStage=DATA_STAGE; //進入到數(shù)據(jù)階段
//CBW中偏移量為15的字段為命令的類型
switch(CBW[15])
{
case INQUIRY: //INQUIRY命令
#ifdef DEBUG0
Prints("查詢命令。返回數(shù)據(jù):\r\n");
#endif
pEp2SendData=DiskInf; //返回磁盤信息
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
SetCsw(Ep2DataLength-sizeof(DiskInf),0); //設置剩余字節(jié)數(shù)以及狀態(tài)成功
if(Ep2DataLength>sizeof(DiskInf)) //如果請求的數(shù)據(jù)比實際的要長
{
Ep2DataLength=sizeof(DiskInf); //則只返回實際的長度
}
Ep2SendData(); //返回數(shù)據(jù)
break;
case READ_FORMAT_CAPACITIES: //讀格式化容量
#ifdef DEBUG0
Prints("讀格式化容量命令。返回數(shù)據(jù):\r\n");
#endif
pEp2SendData=MaximumCapacity; //返回最大格式化容量信息
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
SetCsw(Ep2DataLength-sizeof(MaximumCapacity),0); //設置剩余字節(jié)數(shù)以及狀態(tài)成功
if(Ep2DataLength>sizeof(MaximumCapacity)) //如果請求的數(shù)據(jù)比實際的要長
{
Ep2DataLength=sizeof(MaximumCapacity); //則只返回實際的長度
}
Ep2SendData(); //返回數(shù)據(jù)
break;
case READ_CAPACITY: //讀容量命令
#ifdef DEBUG0
Prints("讀容量命令。返回數(shù)據(jù):\r\n");
#endif
pEp2SendData=DiskCapacity; //返回磁盤容量
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
SetCsw(Ep2DataLength-sizeof(DiskCapacity),0); //設置剩余字節(jié)數(shù)以及狀態(tài)成功
if(Ep2DataLength>sizeof(DiskCapacity)) //如果請求的數(shù)據(jù)比實際的要長
{
Ep2DataLength=sizeof(DiskCapacity); //則只返回實際的長度
}
Ep2SendData(); //返回數(shù)據(jù)
break;
case READ_10: //READ(10)命令
#ifdef DEBUG0
Prints("READ(10)命令。返回數(shù)據(jù):\r\n");
#endif
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
ByteAddr=GetLba()*512; //獲取字節(jié)地址,字節(jié)地址為邏輯塊地址乘以每塊大小
SetCsw(0,0); //設置剩余字節(jié)數(shù)為0,狀態(tài)成功
GetDiskData(); //獲取需要返回的數(shù)據(jù)
Ep2SendData(); //返回數(shù)據(jù)
break;
case WRITE_10: //WRITE(10)命令
#ifdef DEBUG0
Prints("WRITE(10)命令。輸出數(shù)據(jù):\r\n");
#endif
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
SetCsw(0,0); //設置剩余字節(jié)數(shù)為0,狀態(tài)成功
break;
case REQUEST_SENSE: //該命令詢問前一個命令執(zhí)行失敗的原因
#ifdef DEBUG0
Prints("REQUEST SENSE命令。返回SENSE數(shù)據(jù)(無效命令):\r\n");
#endif
pEp2SendData=SenseData; //返回探測數(shù)據(jù)
Ep2DataLength=GetDataTransferLength(); //獲取需要返回的長度
SetCsw(Ep2DataLength-sizeof(SenseData),0); //設置剩余字節(jié)數(shù)以及狀態(tài)成功
if(Ep2DataLength>sizeof(SenseData)) //如果請求的數(shù)據(jù)比實際的要長
{
Ep2DataLength=sizeof(SenseData); //則只返回實際的長度
}
Ep2SendData(); //返回數(shù)據(jù)
break;
case TEST_UNIT_READY: //測試磁盤是否準備好
Ep2DataLength=0; //設置長度為0,發(fā)送數(shù)據(jù)將返回CSW
SetCsw(0,0); //設置CSW為成功
Ep2SendData(); //返回CSW
break;
default: //其它命令不認,返回執(zhí)行失敗
if(CBW[12]&0x80) Ep2DataLength=1; //如果為輸入請求,則隨便返回1字節(jié)
else Ep2DataLength=0; //否則為輸出請求,則設置長度為0,直接返回CSW
SetCsw(GetDataTransferLength()-Ep2DataLength,1); //設置CSW為失敗
Ep2SendData(); //返回CSW
break;
}
}
////////////////////////End of function//////////////////////////////
/********************************************************************
函數(shù)功能:處理輸出數(shù)據(jù)。
入口參數(shù):無。
返 回:無。
備 注:無。
********************************************************************/
void ProcScsiOutData(void)
{
uint8 Len;
//讀端點2數(shù)據(jù)
Len=D12ReadEndpointBuffer(4,EP2_SIZE,Ep2Buffer);
Ep2DataLength-=Len;
//清除端點緩沖區(qū)
D12ClearBuffer();
//由于沒有存儲器,這里將緩沖區(qū)清0模擬數(shù)據(jù)處理
while(Len)
{
Ep2Buffer[Len]=0; //緩沖區(qū)清0
Len--;
}
//數(shù)據(jù)傳輸完畢,進入到狀態(tài)階段
if(Ep2DataLength==0)
{
//此時Ep2DataLength為0,并且處于數(shù)據(jù)階段,調用發(fā)送數(shù)據(jù)函數(shù)將返回CSW
Ep2SendData();
}
}
////////////////////////End of function//////////////////////////////
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -