<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
三、USB函数库分析-核心处理函数usb_core.c
1、从三大核心函数开始
对四个最重要函数的声明:
u8 Setup0_Process(void);
u8 Out0_Process(void);
u<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />8 In0_Process(void);
(1)Setup0_Process(void)的处理过程
u8 Setup0_Process(void)
{
pBuf.b = PMAAddr + (u8 *)(_GetEPRxAddr(ENDP0) * 2);
//在SETUP中断时,它后面所跟随的请求数据包,已经存入了端点0的接收缓冲区,这个表达式是取得该地址。
if (pInformation->ControlState != PAUSE)
{
pInformation->USBbmRequestType = *pBuf.b++; /请求特征吗,一个字节,表明数据方向、发送者、请求接收对象 */
pInformation->USBbRequest = *pBuf.b++; /*具体请求 */
pBuf.w++; /* 后面两个字节为空,跳过去 */
pInformation->USBwValue = ByteSwap(*pBuf.w++); /* wValue */
pBuf.w++; /*后面两个字节为空,跳过去*/
pInformation->USBwIndex = ByteSwap(*pBuf.w++); /* wIndex */
pBuf.w++; /*后面两个字节为空,跳过去*/
pInformation->USBwLength = *pBuf.w; /* wLength */
}
pInformation->ControlState = SETTING_UP; //设置状态“正在SETUP”
if (pInformation->USBwLength == 0)
{
NoData_Setup0(); //像设置地址、设置配置这些请求是没有数据过程的,调用该函数处理。
}
else
{
Data_Setup0();//像获取描述符,设置报告这些是带数据的。
}
return Post0_Process();
}
(2)NoData_Setup0 (void)的处理过程
if(Type_Recipient== (STANDARD_REQUEST | DEVICE_RECIPIENT))
如果是标准请求STANDARD_REQUEST,且请求对象为设备DEVICE_RECIPIENT,则在这个代码块里进行处理。
这个代码块里处理三个标准请求:设置配置、设置地址和设置设备特性(好像是远程唤醒特性)。都是调用相应的标准设置函数实现的。比如:
Result = Standard_SetConfiguration()
第二个代码块:else if (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
这个代码块下面的请求是发往接口的:只支持设置接口命令。
第三个代码块:else if (Type_Recipient == (STANDARD_REQUEST | ENDPOINT_RECIPIENT))
第三个代码块的请求是发往端点的:包括设置特性和清除特性两个请求。
如果请求不属于以上部分,则交由下面处理:
if (Result != USB_SUCCESS)
{ //交由用户提供的类处理函数Class_NoData_Setup处理。
Result = (*pProperty->Class_NoData_Setup)(RequestNo);
if (Result == USB_NOT_READY)
{ //如果用户层也不支持这个请求,则SETUP失败。
ControlState = PAUSE;
goto exit_NoData_Setup0;
}
}
如果请求被成功执行:
ControlState = WAIT_STATUS_IN;/* 进入状态过程,等待主机的IN指令,然后返回一个0字节的状态数据包。 */
USB_StatusIn();
这个函数出口时,ControlState可能是三种状态。
Stalled:表明本次请求失败,遇到IN、OUT包均不响应了,直到下一个SETUP到来。
PAUSE:USB设备不再接受请求,枚举失败。
WAIT_STATUS_IN:本次处理成功,期待主机的“IN包”,进入状态过程。实际上是在“IN包”的处理过程中,真正处理设置地址的请求。
从这个函数回到Setup0_Process(void)后,还要调用Post0_Process(void)。这里Post0_Process(void)的主要作用是如果处理状态变为,则将端点状态设置为“stalled”,不再响应“IN”和“OUT”包,只响应“SETUP”包。
(3)Data_Setup0 (void)的处理过程
如果请求数据的长度不等于0,则进入Data_Setup0进行处理。
if (Request_No == GET_DESCRIPTOR)
{
if(Type_Recipient==(STANDARD_REQUEST| EVICE_RECIPIENT))
{
u8 wValue1 = pInformation->USBwValue1;
if (wValue1 == DEVICE_DESCRIPTOR)
{
CopyRoutine = pProperty->GetDeviceDescriptor;
}
这个代码块是请求描述符的,标准请求三种情况:设备描述符、配置描述符和字符串描述符。因为这些描述符都有具体应用和用户提供,所以它需要调用用户的相应函数pProperty->GetDeviceDescriptor等等。
当然这个函数并不实际承担数据转移的任务,它只是获取描述符长度(还剩多少字节要传输)、对当前数据传输进行定位(偏移到多少了)。
else if ((Request_No = GET_STATUS) && (pInformation->USBwValue = 0)
&& (pInformation->USBwLength == 0x0002)
&& (pInformation->USBwIndex1 == 0))
这个代码块实现获取状态的请求,返回数据要求2个字节。获取状态可以分别针对设备、接口和端点。
接下来两个代码块是获取配置和获取接口的。这里就不详细分析了。
if (CopyRoutine) //如果是标准请求,且参数正确,执行这个代码块。
{
pInformation->Ctrl_Info.Usb_wOffset = wOffset; //初始偏移为0
pInformation->Ctrl_Info.CopyData = CopyRoutine;
(*CopyRoutine)(0); //这个实际获得需要传输的数据的长度。
Result = USB_SUCCESS;
}
Else //如果是类特殊请求,则交由用户处理。
{
Result = (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
if (Result == USB_NOT_READY)
{ //如果用户也不支持,通信失败。
pInformation->ControlState = PAUSE;
return;
}
}
接下来是确定数据阶段的传输方向,主机可能是要发送数据:如Set_Descripter,也可能是要取得数据,如:Get_Descripter。
if (ValBit(pInformation->USBbmRequestType, 7))
{
vu32 wLength = pInformation->USBwLength; //主机要获取的长度。
if (pInformation->Ctrl_Info.Usb_wLength > wLength)
{ //如果实际长度超过了需求长度,把实际长度减少为主机需求长度
pInformation->Ctrl_Info.Usb_wLength = wLength;
}
Else if (pInformation->Ctrl_Info.Usb_wLength < pInformation->USBwLength) //实际能传输数目要小于主机需求长度
{
if (pInformation->Ctrl_Info.Usb_wLength< Property->MaxPacketSize)
{
Data_Mul_MaxPacketSize = FALSE;
}
Else if((pInformation->Ctrl_Info.Usb_wLength% pProperty->MaxPacketSize) == 0)
{//如果实际长度是最大数据包长的整数倍,要传输0字节数据包。
Data_Mul_MaxPacketSize = TRUE;
}
} //大多数情况下,主机指定长度应该与实际传输长度要对应。
pInformation->Ctrl_Info.PacketSize = pProperty->MaxPacketSize;
DataStageIn(); //如果数据阶段是“IN”,则现在就要准备好数据,设置端点TX状态。
}
else
{ //如果数据阶段是“OUT”则主要在“Out0_Process()”函数中处理。这里只需要改变状态,使能端点接收就行了。
pInformation->ControlState = OUT_DATA;
vSetEPRxStatus(EP_RX_VALID); /* enable for next data reception */
}
(4)DataStageIn()的处理过程
这个函数主要是将用户缓冲区的数据(以描述符为最典型),复制到控制端点0“TxADDR”所指向的端点数据输出缓冲区。
void DataStageIn(void)
{
ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;
u32 save_wLength = pEPinfo->Usb_wLength; //剩下的需要传输的长度。
u32 ControlState = pInformation->ControlState;
u8 *DataBuffer;
u32 Length;
if ((save_wLength == 0) && (ControlState == LAST_IN_DATA))
{
if(Data_Mul_MaxPacketSize == TRUE)
{
Send0LengthData(); //发0字节数据包。
ControlState = LAST_IN_DATA;
Data_Mul_MaxPacketSize = FALSE;
}//0字节数据包也发了,进入状态阶段。
else
{ //已经传输完成,进入状态阶段。
ControlState = WAIT_STATUS_OUT;
vSetEPTxStatus(EP_TX_STALL);
}
goto Expect_Status_Out;
}
Length = pEPinfo->PacketSize;
ControlState = (save_wLength <= Length) ? LAST_IN_DATA : IN_DATA; //这里根据剩余要发送的字节数与每次能发送的最大字节数相比,确认是否最后一次发送。
if (Length > save_wLength)
{
Length = save_wLength; //length现在为实际传输字节数。可能是用户缓冲区还剩余的字节数,也可能就等于数据包长。
}//为了使概念更清晰一些,我这里举出具体数据。比如主机索取255个字节,描述符实际长度为129字节,包长为64个字节。判断后的状态为“IN_DATA”.
则到了这儿Length = 64,也就是本次传输64个字节。
DataBuffer = (*pEPinfo->CopyData)(Length); //第一次传输时,偏移为0,DataBuffer指向缓冲区开头。
UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length);
//复制64个字节到端点发送缓冲区。只要主机的“IN”一来,数据马上返回给主机。
SetEPTxCount(ENDP0, Length);
pEPinfo->Usb_wLength -= Length; //此时剩余长度为65
pEPinfo->Usb_wOffset += Length;//偏移指向64
vSetEPTxStatus(EP_TX_VALID);
USB_StatusOut();//这里是指用户可以取消“IN数据过程”,这个是在“Out0_Process()”中完成的。
Expect_Status_Out:
pInformation->ControlState = ControlState;
}
(5)In0_Process()的处理过程
数据过程从上面蓝色的文字给出的状态开始:
在主机的“IN”令牌包发出后,首先取走了“64个字节”,然后进入中断,并调用In0_Process()。
if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))
{
DataStageIn(); //继续调用它准备下面的数据。
//第二次调用时,用户缓冲区指针指向64,实际传输长度64,剩余长度变为1,偏移指向128,状态还是“IN_DATA”。
第三次调用时,状态变成“LAST_DATA”,缓冲区指向128,实际传输长度只有1个字节。再次发生“IN”的时候,就会转入状态阶段。
ControlState = pInformation->ControlState;
}
(6)Out0_Process()的处理过程
if((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA))
{
DataStageOut();
ControlState = pInformation->ControlState; /* may be changed outside the function */
}
这个函数最主要就是调用进行数据处理,将端点接收缓冲区收到的数据复制到用户缓冲区。
(7)DataStageOut()的处理过程
save_rLength = pEPinfo->Usb_rLength;
if (pEPinfo->CopyData && save_rLength)
{
Length = pEPinfo->PacketSize; //初始化为数据包长。
if (Length > save_rLength)
{ //实际不需要这么大,这长度改为实际长度。
Length = save_rLength;
} //如果实际长度大于等于包长,则复制一个包长。
Buffer = (*pEPinfo->CopyData)(Length); //定位用户缓冲区。
pEPinfo->Usb_rLength -= Length;
pEPinfo->Usb_rOffset += Length;
PMAToUserBufferCopy(Buffer, GetEPRxAddr(ENDP0), Length);
}
if (pEPinfo->Usb_rLength >= pEPinfo->PacketSize)
{ //根据剩余长度确定控制传输状态。
pInformation->ControlState = OUT_DATA;
}
else
{ if (pEPinfo->Usb_rLength > 0)
{ //还有数据但是小于一个包。则下一次为最后一次传输。
pInformation->ControlState = LAST_OUT_DATA;
}
else if (pEPinfo->Usb_rLength == 0)
{ //没有数据了,期待状态过程。
pInformation->ControlState = WAIT_STATUS_IN;
USB_StatusIn(); //先把0字节数据包准备好。
}
}
通过把这几个核心函数详细的看一遍,我对于USB控制传输阶段的流程感觉已经比较熟悉了。
2、其它处理函数
主要包括:
RESULT Standard_SetEndPointFeature(void);
RESULT Standard_SetDeviceFeature(void);
u8 *Standard_GetConfiguration(u16 Length);
RESULT Standard_SetConfiguration(void);
u8 *Standard_GetInterface(u16 Length);
RESULT Standard_SetInterface(void);
u8 *Standard_GetDescriptorData(u16 Length, PONE_DESCRIPTOR pDesc);
u8 *Standard_GetStatus(u16 Length);
RESULT Standard_ClearFeature(void);
void SetDeviceAddress(u8);
代码都不多,我就简单的浏览了一遍,就不详细分析了。
yupin1ger_144042119 2012-9-12 15:59