原创 U盘实现流程跟踪分析01

2010-4-27 21:51 6043 15 16 分类: MCU/ 嵌入式

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

 


 


一、追踪USB大容量设备的实现流程


 


1、从main.c开始


1main函数的执行流程


  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.cMass_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 transparentSCSI传输*/


    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_Datascsi_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接收又被使能,重新进入接收命令状态


 


 

文章评论1条评论)

登录后参与讨论

用户156578 2010-5-13 10:35

想请教一下博主是怎样进行流程跟踪的,谢谢
相关推荐阅读
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大容量设备的实现...
我要评论
1
15
关闭 站长推荐上一条 /2 下一条