原创 USB的“JoyStickMouse”源代码分析03

2010-4-24 17:57 5501 15 16 分类: MCU/ 嵌入式

<?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);


1Setup0_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();


}


 


2NoData_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表明本次请求失败,遇到INOUT包均不响应了,直到下一个SETUP到来。


PAUSEUSB设备不再接受请求,枚举失败。


WAIT_STATUS_IN本次处理成功,期待主机的“IN包”,进入状态过程。实际上是在“IN包”的处理过程中,真正处理设置地址的请求。


 


从这个函数回到Setup0_Process(void)后,还要调用Post0_Process(void)。这里Post0_Process(void)的主要作用是如果处理状态变为,则将端点状态设置为“stalled”,不再响应“IN”和“OUT”包,只响应“SETUP”包


 


3Data_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 */


  }


 


4DataStageIn()的处理过程


这个函数主要是将用户缓冲区的数据(以描述符为最典型),复制到控制端点0TxADDR”所指向的端点数据输出缓冲区。


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); //第一次传输时,偏移为0DataBuffer指向缓冲区开头。


  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;


}


 


5In0_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;


  }


 


6Out0_Process()的处理过程


  if((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA))


  {


    DataStageOut();


    ControlState = pInformation->ControlState; /* may be changed outside the function */


  }


这个函数最主要就是调用进行数据处理,将端点接收缓冲区收到的数据复制到用户缓冲区。


 


7DataStageOut()的处理过程


  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);


 


代码都不多,我就简单的浏览了一遍,就不详细分析了。


 


 


 


 

文章评论1条评论)

登录后参与讨论

yupin1ger_144042119 2012-9-12 15:59

hao
相关推荐阅读
nthq2004 2010-05-08 20:04
USB自定义设备驱动02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  本来还想编写应用程序测试一下自定...
nthq2004 2010-05-07 21:35
USB自定义设备驱动01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、USB设备驱动入门1、学习目...
nthq2004 2010-05-04 21:01
智林开发板上实现自定义的USB HID设备
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、自定义HID设备的相关概念1...
nthq2004 2010-05-01 21:58
U盘例程在智林开发板上的移植
 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一、移植前的准备工作1、有哪些操...
nthq2004 2010-04-30 19:19
U盘实现流程跟踪分析02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   二、追踪USB大容量设备的实现...
nthq2004 2010-04-27 21:51
U盘实现流程跟踪分析01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   一、追踪USB大容量设备的实现...
我要评论
1
15
关闭 站长推荐上一条 /2 下一条