作者:milershao 剛好幾天前有個客戶STM32F105的OTG模塊操作U盤,發(fā)現(xiàn)個別U盤電腦能正常使用,而STM32F105OTG模塊無法識別。ST官方相關(guān)FAE有做相關(guān)應(yīng)用筆記,借花獻佛并致謝,轉(zhuǎn)過來與大家分享之。 問題回顧: 有客戶使用 STM32F2的 OTG 庫中的 U 盤主機例程在連接U盤時,有些 U 盤不能識別,甚至出現(xiàn)操作死機的情況�,F(xiàn)就針對版本ST官方提供的 V2.1.0 的 USB 主機庫中的 MSC Host 例程做一些修改,以能夠兼容更多U 盤。 
問題調(diào)研: 1、有些 U 盤在收到 BOT_RESET 這個 MSC 類相關(guān)命令時,就不再有反應(yīng)了:設(shè)備一直對后續(xù)主機發(fā)來的 IN 令牌回復(fù) NAK 。通過對 BOT_RESET 命令的調(diào)查得知,USB 規(guī)范為大容量設(shè)備定義了兩種類型的復(fù)位:USB 端口復(fù)位和大容量設(shè)備BOT 復(fù)位。 然而USB 規(guī)范并未規(guī)定這兩種復(fù)位對應(yīng)到 SCSI 命令中是做什么操作。通常設(shè)備把 USB端口復(fù)位映射成 SCSI 的硬復(fù)位;有些設(shè)備把 BOT復(fù)位映射到設(shè)備的 hard reset,有的僅是映射成邏輯單元復(fù)位。該條命令通常用于主機對在 BOT 通信中出錯的設(shè)備進行復(fù)位恢復(fù)。但是需要指出的是,有些 U 盤并沒有完全遵守大容量規(guī)范,比如不實現(xiàn)對 BOT_RESET 命令的支持。在這種情況下,設(shè)備一般會回復(fù) STALL,那么主機需要發(fā)送 Set Port Feature(PORT_RESET)來復(fù)位設(shè)備所連的 Hub 端口。但是這個 U 盤并沒有回復(fù) STALL,且 U 盤是直接連在 STM32 USB 主機上的,并沒有連接 Hub。于是,參照該 U 盤在 Window 下的連接操作的過程文件(tracer file),發(fā)現(xiàn)在 枚舉完成后,Windows 并沒有發(fā)送 BOT_RESET 命令,而是直接就發(fā)送 GET_Max_Lun 命令來獲取 U 盤的邏輯盤符個數(shù)。 于是在STM32 的 USB Host 例程中注釋掉 BOT_RESET 命令,果然可以正確操作了。修改代碼如下:
USBH_MSC_Handle() {...... { switch(USBH_MSC_BOTXferParam.MSCState) { case USBH_MSC_BOT_INIT_STATE: USBH_MSC_Init(pdev); //USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_RESET; USBH_MSC_BOTXferParam.MSCState = USBH_MSC_GET_MAX_LUN; break; case… 2、還有些 U 盤在收到 Get_Max_Lun 命令返回 STALL,但是 U 抓到的 tracer 文件如圖 盤主機就走不下去了。 
經(jīng)查閱,有些只包含一個邏輯盤符的 U 盤可以對該命令回復(fù)數(shù)值 0 或者直接回復(fù) STALL。 那么對于 USB 主機來說,在收到了該條命令的 STALL 回復(fù)后就應(yīng)該第一:認為該 U 盤僅包含 一個邏輯盤符;第二:對 STALL 應(yīng)答進行處理,然后繼續(xù)下面的命令流程。 通過 USB2.0 協(xié)議規(guī)范(章節(jié) 9.2.7),我們得知在控制傳輸(Control Transfer)過程中,當設(shè)備收到的命令自己不支持或者不適合設(shè)備當前的設(shè)置,就認為是命令出錯。那么設(shè)備通過在接 下來的數(shù)據(jù)階段或者狀態(tài)階段回復(fù) STALL 應(yīng)答來告知主機這個錯誤。這種“協(xié)議 STALL”是 控制傳輸特有的;這樣 STALL 的狀態(tài),會在下一個控制傳輸(Setup 令牌)的到來而解除。 我們看看 Windows 對 U 盤這樣的回復(fù)是怎么處理,tracer 文件抓圖如下:主機通過 Clear Feature 命令,參數(shù) EP_Halt(這是一個控制傳輸)來把設(shè)備方端點的 Halt feature 清除掉。 
從代碼里可以看到,例程是有做這方面的處理的: 如果主機發(fā)送的 Get_Max_Lun 命令不被設(shè) 備支持,則將 MSCState 狀態(tài)設(shè)定成 CTRL_ERROR_STATE,發(fā)送Clear Feature 的命令,以及隨后 的 Test_Unit_Ready 命令。
USBH_MSC_Handle() ...... { switch(USBH_MSC_BOTXferParam.MSCState) { case USBH_MSC_GET_MAX_LUN: /* Issue GetMaxLUN request */ status = USBH_MSC_GETMaxLUN(pdev, phost); if(status == USBH_OK ) { MSC_Machine.maxLun = *(MSC_Machine.buff) ; /* If device has more that one logical unit then it is not supported */ if((MSC_Machine.maxLun > 0) && (maxLunExceed == FALSE)) { maxLunExceed = TRUE; pphost->usr_cb->DeviceNotSupported(); break; } USBH_MSC_BOTXferParam.MSCState = USBH_MSC_TEST_UNIT_READY; } if(status == USBH_NOT_SUPPORTED ) { /* If the Command has failed, then we need to move to Next State, after STALL condition is cleared by Control-Transfer */ USBH_MSC_BOTXferParam.MSCStateBkp = USBH_MSC_TEST_UNIT_READY; /* a Clear Feature should be issued here */ USBH_MSC_BOTXferParam.MSCState = USBH_MSC_CTRL_ERROR_STATE; } break; …… 但是從抓到的 tracer 文件卻看不到主機走到了發(fā)送 Test_Unit_Ready 的命令。經(jīng)過調(diào)試、代碼跟蹤,終于定位在 USBH_HandleControl()中。對 CTRL_DATA_IN_WAIT 的處理,當收到 URB_STALL 的應(yīng)答后,沒有給 phost 的 Control.state 賦值成 CTRL_STALLED! 
USBH_HandleControl( ) { ...... switch (phost->Control.state) { case CTRL_DATA_IN_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); /* check is DATA packet transfered successfully */ if (URB_Status == URB_DONE) { phost->Control.state = CTRL_STATUS_OUT; } /* manage error cases*/ if (URB_Status == URB_STALL) { /* In stall case, return to previous machine state*/ phost->gState = phost->gStateBkp; phost->Control.state = CTRL_STALLED; // } else if ......
3、還有一點需要注意的是:本版的 U 盤主機例程,暫時不支持多盤符 U 盤。 剛好幾天前有個客戶STM32F105的OTG模塊操作U盤,發(fā)現(xiàn)個別U盤電腦能正常使用,而STM32F105OTG模塊無法識別。ST官方相關(guān)FAE有做相關(guān)應(yīng)用筆記,借花獻佛并致謝,轉(zhuǎn)過來與大家分享之。 問題回顧: 有客戶使用 STM32F2的 OTG 庫中的 U 盤主機例程在連接U盤時,有些 U 盤不能識別,甚至出現(xiàn)操作死機的情況�,F(xiàn)就針對版本ST官方提供的 V2.1.0 的 USB 主機庫中的 MSC Host 例程做一些修改,以能夠兼容更多U 盤。 
問題調(diào)研: 1、有些 U 盤在收到 BOT_RESET 這個 MSC 類相關(guān)命令時,就不再有反應(yīng)了:設(shè)備一直對后續(xù)主機發(fā)來的 IN 令牌回復(fù) NAK 。通過對 BOT_RESET 命令的調(diào)查得知,USB 規(guī)范為大容量設(shè)備定義了兩種類型的復(fù)位:USB 端口復(fù)位和大容量設(shè)備BOT 復(fù)位。 然而USB 規(guī)范并未規(guī)定這兩種復(fù)位對應(yīng)到 SCSI 命令中是做什么操作。通常設(shè)備把 USB端口復(fù)位映射成 SCSI 的硬復(fù)位;有些設(shè)備把 BOT復(fù)位映射到設(shè)備的 hard reset,有的僅是映射成邏輯單元復(fù)位。該條命令通常用于主機對在 BOT 通信中出錯的設(shè)備進行復(fù)位恢復(fù)。但是需要指出的是,有些 U 盤并沒有完全遵守大容量規(guī)范,比如不實現(xiàn)對 BOT_RESET 命令的支持。在這種情況下,設(shè)備一般會回復(fù) STALL,那么主機需要發(fā)送 Set Port Feature(PORT_RESET)來復(fù)位設(shè)備所連的 Hub 端口。但是這個 U 盤并沒有回復(fù) STALL,且 U 盤是直接連在 STM32 USB 主機上的,并沒有連接 Hub。于是,參照該 U 盤在 Window 下的連接操作的過程文件(tracer file),發(fā)現(xiàn)在 枚舉完成后,Windows 并沒有發(fā)送 BOT_RESET 命令,而是直接就發(fā)送 GET_Max_Lun 命令來獲取 U 盤的邏輯盤符個數(shù)。 于是在STM32 的 USB Host 例程中注釋掉 BOT_RESET 命令,果然可以正確操作了。修改代碼如下:
USBH_MSC_Handle() {...... { switch(USBH_MSC_BOTXferParam.MSCState) { case USBH_MSC_BOT_INIT_STATE: USBH_MSC_Init(pdev); //USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_RESET; USBH_MSC_BOTXferParam.MSCState = USBH_MSC_GET_MAX_LUN; break; case… 2、還有些 U 盤在收到 Get_Max_Lun 命令返回 STALL,但是 U 抓到的 tracer 文件如圖 盤主機就走不下去了。 
經(jīng)查閱,有些只包含一個邏輯盤符的 U 盤可以對該命令回復(fù)數(shù)值 0 或者直接回復(fù) STALL。 那么對于 USB 主機來說,在收到了該條命令的 STALL 回復(fù)后就應(yīng)該第一:認為該 U 盤僅包含 一個邏輯盤符;第二:對 STALL 應(yīng)答進行處理,然后繼續(xù)下面的命令流程。 通過 USB2.0 協(xié)議規(guī)范(章節(jié) 9.2.7),我們得知在控制傳輸(Control Transfer)過程中,當設(shè)備收到的命令自己不支持或者不適合設(shè)備當前的設(shè)置,就認為是命令出錯。那么設(shè)備通過在接 下來的數(shù)據(jù)階段或者狀態(tài)階段回復(fù) STALL 應(yīng)答來告知主機這個錯誤。這種“協(xié)議 STALL”是 控制傳輸特有的;這樣 STALL 的狀態(tài),會在下一個控制傳輸(Setup 令牌)的到來而解除。 我們看看 Windows 對 U 盤這樣的回復(fù)是怎么處理,tracer 文件抓圖如下:主機通過 Clear Feature 命令,參數(shù) EP_Halt(這是一個控制傳輸)來把設(shè)備方端點的 Halt feature 清除掉。 
從代碼里可以看到,例程是有做這方面的處理的: 如果主機發(fā)送的 Get_Max_Lun 命令不被設(shè) 備支持,則將 MSCState 狀態(tài)設(shè)定成 CTRL_ERROR_STATE,發(fā)送Clear Feature 的命令,以及隨后 的 Test_Unit_Ready 命令。
USBH_MSC_Handle() ...... { switch(USBH_MSC_BOTXferParam.MSCState) { case USBH_MSC_GET_MAX_LUN: /* Issue GetMaxLUN request */ status = USBH_MSC_GETMaxLUN(pdev, phost); if(status == USBH_OK ) { MSC_Machine.maxLun = *(MSC_Machine.buff) ; /* If device has more that one logical unit then it is not supported */ if((MSC_Machine.maxLun > 0) && (maxLunExceed == FALSE)) { maxLunExceed = TRUE; pphost->usr_cb->DeviceNotSupported(); break; } USBH_MSC_BOTXferParam.MSCState = USBH_MSC_TEST_UNIT_READY; } if(status == USBH_NOT_SUPPORTED ) { /* If the Command has failed, then we need to move to Next State, after STALL condition is cleared by Control-Transfer */ USBH_MSC_BOTXferParam.MSCStateBkp = USBH_MSC_TEST_UNIT_READY; /* a Clear Feature should be issued here */ USBH_MSC_BOTXferParam.MSCState = USBH_MSC_CTRL_ERROR_STATE; } break; …… 但是從抓到的 tracer 文件卻看不到主機走到了發(fā)送 Test_Unit_Ready 的命令。經(jīng)過調(diào)試、代碼跟蹤,終于定位在 USBH_HandleControl()中。對 CTRL_DATA_IN_WAIT 的處理,當收到 URB_STALL 的應(yīng)答后,沒有給 phost 的 Control.state 賦值成 CTRL_STALLED! 
USBH_HandleControl( ) { ...... switch (phost->Control.state) { case CTRL_DATA_IN_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); /* check is DATA packet transfered successfully */ if (URB_Status == URB_DONE) { phost->Control.state = CTRL_STATUS_OUT; } /* manage error cases*/ if (URB_Status == URB_STALL) { /* In stall case, return to previous machine state*/ phost->gState = phost->gStateBkp; phost->Control.state = CTRL_STALLED; // } else if ......
3、還有一點需要注意的是:本版的 U 盤主機例程,暫時不支持多盤符 U 盤。 |