分享

U盘实现流程跟踪分析01

 wfsy1983 2011-01-25
一、追踪USB大容量设备的实现流程
 
1、从main.c开始
(1)main函数的执行流程
  Set_System();  //设置时钟、端口等。
  Set_USBClock();  //设置usb的时钟
  USB_Interrupts_Config();  //设置中断
  Led_Config();  //设置所使用的到的灯。
  MSD_Init();  //SD卡初始化
  Get_Medium_Characteristics();  //获取SD块总数、每块字节数。
  USB_Init();  //USB_init.c提供的初始化函数。从这里开始USB设备被主机检测到。
  while (1)
  {  //USB的工作都是在中断中完成的,主执行流程什么也没做。
  }
(2)与鼠标例程不同的地方
在中断配置中,使能了USB高优先级中断。
  NVIC_InitStructure.NVIC_IRQChannel= USB_HP_CAN_TX_IRQChannel;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
用到了几个灯指示,这个我的开发板上用不到,就不详细看了。
 
MSD_Init(),是对SD卡进行初始化。该函数在msd.c中,我看了一下它的SD卡实现的代码,比我的SD函数代码齐整多了。以后有时间要把我的USB驱动好好的整理一下。不过现在就先不管了。
 
接下来这个函数获取SD卡的容量,这样的函数我在SD卡驱动中也实现了,改变一下调用方式就行了。
 
USB_Init()函数在usb_init.c库函数中,但它最终会调用user_prop.c宏的用户初始化例程。下面就追踪进去看一看。
 
(3)大容量存储设备的初始化
void MASS_init()
{
  pInformation->Current_Configuration = 0;
  PowerOn();  连接电缆主机很快发总线复位。
  _SetISTR(0);
  wInterrupt_Mask = IMR_MSK;
  _SetCNTR(wInterrupt_Mask);  开启复位和传输中断。
  pInformation->Current_Feature = MASS_ConfigDescriptor[7];
  while (pInformation->Current_Configuration == 0)
  {
    NOP_Process();
  }
  bDeviceState = CONFIGURED;  //这句执行完成后,设备处于已配置状态。我先在这里加一句调试语句。
#if usb_debug
Uart_PutString(“设备已配置”);
#endif
}
 
2、进入复位中断
(1)先列出中断处理代码
发生总线复位中断以后,处理是在usb_prop.c的Mass_Reset()函数中完成的。
void MASS_Reset()
{
  Device_Info.Current_Configuration = 0;
  SetBTABLE(BTABLE_ADDRESS);
 
  SetEPType(ENDP0, EP_CONTROL);  //端点0控制端点
  SetEPTxStatus(ENDP0, EP_TX_NAK); //不响应IN
  SetEPRxAddr(ENDP0, ENDP0_RXADDR); //设置接收缓冲区(OUT)
  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); 接收长度。
  SetEPTxAddr(ENDP0, ENDP0_TXADDR); //发送缓冲区(IN)
  Clear_Status_Out(ENDP0);
  SetEPRxValid(ENDP0);  //使能端点0的接收。
 
   SetEPType(ENDP1, EP_BULK); //端点1批量模式
  SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送缓冲区(IN)
  SetEPTxStatus(ENDP1, EP_TX_NAK); 发送不响应。
  SetEPRxStatus(ENDP1, EP_RX_DIS); //接收无效。对OUT无效
 
  SetEPType(ENDP2, EP_BULK); //端点2批量模式
  SetEPRxAddr(ENDP2, ENDP2_RXADDR); //设置接收缓冲区OUT
  SetEPRxCount(ENDP2, Device_Property.MaxPacketSize);
  SetEPRxStatus(ENDP2, EP_RX_VALID);
  SetEPTxStatus(ENDP2, EP_TX_DIS); //发送无效,对IN无效
 
  SetDeviceAddress(0);  //使能USB接口模块。
  CBW.dSignature = BOT_CBW_SIGNATURE;
  Bot_State = BOT_IDLE; //命令状态机初始化为空闲状态
}
在这里没有我没有看到将批量端点设置为双缓冲模式的迹象,难道这个例程没有用它?
 
3、进入枚举过程
因为在鼠标例程中已经详细分析过枚举过程,这里主要是大容量设备枚举过程中不同的地方做一下分析。
(1)获取设备描述符、设置地址。
(2)获取配置描述符
(3)获取配置描述符集合,这里主要讲接口描述符分析一下。
    0x09,   /* bLength: Interface Descriptor size */
    0x04,   /* bDescriptorType: */
    0x00,   /* bInterfaceNumber: Number of Interface */
    0x00,   /* bAlternateSetting: Alternate setting */
    0x02,   /* bNumEndpoints*/  使用两个端点
    0x08,   /* bInterfaceClass: MASS STORAGE Class,大容量存储类 */
    0x06,   /* bInterfaceSubClass : SCSI transparent,SCSI传输*/
    0x50,   /* nInterfaceProtocol ,仅批量传输*/
    4,          /* iInterface: */
 
(4)获取字符串描述符
(5)类请求实现:
类获取逻辑盘:
一般返回0
类请求复位:
    ClearDTOG_TX(ENDP1);
    ClearDTOG_RX(ENDP2);
    CBW.dSignature = BOT_CBW_SIGNATURE;
    Bot_State = BOT_IDLE;
 
(6)设置配置
在用户设置的回调函数中,又调用
void Mass_Storage_SetConfiguration(void)
{
  if (pInformation->Current_Configuration)
  {
    ClearDTOG_TX(ENDP1);
    ClearDTOG_RX(ENDP2);
    Bot_State = BOT_IDLE;  }
}
这个工作前面已经做过了。
 
4、主机发命令INQUIRY
(1)首先进入批量输出中断
该中断的回调函数调用Mass_Storage_Out()进行处理。
 
(2)追踪进入Mass_Storage_Out()
void Mass_Storage_Out (void)
{
  u8 CMD;
  CMD = CBW.CB[0]; //
  Data_Len = GetEPRxCount(ENDP2);
  PMAToUserBufferCopy(Bulk_Data_Buff,ENDP2_RXADDR, Data_Len);
 
  switch (Bot_State)
  {
    case BOT_IDLE:
      CBW_Decode();  //第一次收到命令肯定调用这个解码函数。
      break;   //它的作用应该是填充CBW命令块封包结构
    case BOT_DATA_OUT:
      if (CMD == SCSI_WRITE10)
      {
        SCSI_Write10_Cmd();
        break;
      }
}
 
(3)追踪进入CBW_Decode()
这个函数的代码较长,我就不列举在这里了,我就分析一下本次的主要工作。
首先将用户缓冲区的数据复制到命令封包结构里面。
然后准备好状态封包结构:
  CSW.dTag = CBW.dTag;  //这个标志由主机生成,可以用于检查设备是否正确收到该命令。
  CSW.dDataResidue = CBW.dDataLength;
 
 
然后主要是根据命令操作码,调用相应的SCSI命令处理函数。
      switch (CBW.CB[0])
      {
        case SCSI_REQUEST_SENSE:
          SCSI_RequestSense_Cmd ();
          break;
        case SCSI_INQUIRY:
          SCSI_Inquiry_Cmd();
          break;
我这里就列出了两项,实际的命令是很多的。本次主要是查询处理。
 
(4)追踪进入SCSI_Inquiry_Cmd()
以上函数是在usb_bot.c里面,现在跳转到usb_scsi.c里面。
 
这个函数的主要工作是调用Transfer_Data_Request(Inquiry_Data, Inquiry_Data_Length)来完成。
这个Inquiry_Data = Standard_Inquiry_Data,后面这个Standard_Inquiry_Data是scsi_data.c里面定义的一个数据结构,专门用于inquiry命令的返回。
u8 Standard_Inquiry_Data[] =
  {
    0x00,          /* Direct Access Device */
    0x80,          /* RMB = 1: Removable Medium */
    0x02,          /* Version: No conformance claim to standard */
    0x02,           //这里圈圈的书上说应该为 0x01
    36 - 4,         //这里圈圈的书上说应该为 31
    0x00, 0x00, 0x00,     /* SCCS = 1: Storage Controller Component */
 
    'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',//厂商信息
    'S', 'T', 'R', ' ', ' ', 'F', 'l', 'a', 's', 'h', ' ', 'D', 'i', 's', 'k', ' ',//产品信息
    '1', '.', '0', ' '                //版本信息。
  };
(5)追踪进入Transfer_Data_Request ()
 
void Transfer_Data_Request(u8* Data_Pointer, u16 Data_Len)
{
  UserToPMABufferCopy(Data_Pointer, ENDP1_TXADDR, Data_Len);
 
  SetEPTxCount(ENDP1, Data_Len);
  SetEPTxStatus(ENDP1, EP_TX_VALID);
  Bot_State = BOT_DATA_IN_LAST;
  CSW.dDataResidue -= Data_Len;
  CSW.bStatus = CSW_CMD_PASSED; //设置好命令状态封包信息。
}
 
(6)接下来,主机会发IN,取走查询信息。并进入批量输入中断。
在该中断中,将调用函数Mass_Storage_In (void)
 
void Mass_Storage_In (void)
{
  switch (Bot_State)
  {
    case BOT_CSW_Send:
    case BOT_ERROR:
      Bot_State = BOT_IDLE;
      SetEPRxStatus(ENDP2, EP_RX_VALID);/* enable the Endpoint to recive the next cmd*/
      break;
    case BOT_DATA_IN_LAST:
      Set_CSW (CSW_CMD_PASSED, SEND_CSW_ENABLE);
      SetEPRxStatus(ENDP2, EP_RX_VALID);
      break;
    default:
      break;
  }
}
然后设备的命令状态机状态变为Set_CSW()这个函数所设置的状态,一般为BOT_CSW_Send。
 
(7)追踪进入Set_CSW()
在该函数中:
void Set_CSW (u8 CSW_Status, u8 Send_Permission)
{
  CSW.dSignature = BOT_CSW_SIGNATURE;
  CSW.bStatus = CSW_Status;  //命令状态封包数据已经准备好
 
  UserToPMABufferCopy((&CSW),ENDP1_TXADDR, DATA_LENGTH);
  SetEPTxCount(ENDP1, CSW_DATA_LENGTH);
 
  Bot_State = BOT_ERROR;
  if (Send_Permission)
  {
    Bot_State = BOT_CSW_Send;
    SetEPTxStatus(ENDP1, EP_TX_VALID);
  }
 
}
然后,主机再次发IN令牌包,取走命令状态封包。
case BOT_ERROR:
      Bot_State = BOT_IDLE;
      SetEPRxStatus(ENDP2, EP_RX_VALID);
端点2的接收又被使能,重新进入接收命令状态。

本文来源于电气自动化技术网 转载注明出处http://www./dianqi/danpianji/13143.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多