如何從 USB 管道錯誤復原
注意
本文適用於設備驅動器開發人員。 如果您遇到 USB 裝置的困難,請參閱 修正 Windows 中的 USB-C 問題
本文提供當數據傳輸至 USB 管道失敗時,您可以嘗試的步驟相關信息。 本文所述的機制涵蓋大量、中斷和無時針管道上的中止、重設和迴圈埠作業。
USB 用戶端驅動程式會藉由將控制傳輸傳送至預設端點來與其裝置通訊;數據傳輸至裝置的大量、中斷和不時點端點。 有時,這些傳輸可能會因為各種原因而失敗,例如端點中的停滯狀況。 如果傳輸失敗,在清除錯誤狀況之前,相關聯的管道無法處理要求。
針對控制傳輸,USB 驅動程式堆疊會自動清除錯誤狀況。 針對數據傳輸,客戶端必須採取適當的步驟,才能從錯誤狀況中復原。 數據傳輸失敗時,USB 驅動程式堆疊會透過失敗的USBD狀態代碼向客戶端驅動程式報告錯誤。 根據狀態代碼,驅動程式接著可以提供錯誤復原機制。
本文提供透過這些作業進行錯誤復原的指導方針。
- 重設USB管道
- 重設裝置所連線的USB埠
- 迴圈 USB 埠以重新列舉用戶端驅動程式的裝置堆疊
若要清除錯誤狀況,請從重設管道作業開始,並執行更複雜的作業,例如重設埠和迴圈埠,只有在必要的情況下。
關於協調各種復原機制:
用戶端驅動程序必須協調不同的復原作業,並確保在指定時間只使用一個方法。 例如,請考慮具有兩個端點的裝置:大量和中斷。 將一些數據傳輸要求傳送至裝置之後,驅動程式會注意到大量管道上的要求失敗。 若要從這些錯誤復原,驅動程式會重設大量管道。 不過,該作業無法解決傳輸錯誤,而且大量傳輸會繼續失敗。 因此,驅動程式會發出重設USB埠的要求。 同時,傳輸會在中斷管道上開始失敗,然後是重設裝置要求。 若要從中斷傳輸失敗中復原,驅動程式會在中斷管道上發出重設管道要求。 如果未協調這兩項作業,驅動程式可以同時啟動兩個重設裝置作業,因為兩個管道發生失敗。 這些同時作業可能會有問題。
用戶端驅動程式必須確定在指定時間,驅動程式只會執行一個重設埠或迴圈埠作業。 在這些作業期間,重設管道作業不應在任何管道上進行,而且驅動程式不得發出新的重設管道要求。
須知事項
本文使用 內核模式驅動程式架構 (KMDF) 。
必要條件
用戶端驅動程序必須已建立架構 USB 目標裝置物件。
如果您使用visual Studio Professional 2012 Microsoft提供的USB樣本,範本程式代碼會執行這些工作。 範本程式代碼會取得目標裝置物件的句柄,並儲存在裝置內容中。
KMDF 用戶端驅動程式必須藉由呼叫 WdfUsbTargetDeviceCreateWithParameters 方法來取得 WDFUSBDEVICE 句柄。 如需詳細資訊,請參閱瞭解USB用戶端驅動程式程式代碼結構(KMDF)中的。
用戶端驅動程序必須具有架構目標管道物件的句柄。 如需詳細資訊,請參閱 如何列舉 USB 管道。
步驟 1:判斷錯誤狀況的原因
用戶端驅動程式會使用 USB 要求區塊 (URB) 起始數據傳輸。 要求完成之後,USB 驅動程式堆疊會傳回USBD狀態代碼,指出傳輸成功還是失敗。 在失敗中,USBD 程式代碼會指出失敗的原因。
- 如果您藉由呼叫 WdfUsbTargetDeviceSendUrbSynchronously 方法提交 URB,請在方法傳回之後檢查 URB 結構的 Hdr.Status 成員。
- 如果您呼叫 WdfRequestSend 方法以異步方式提交 URB,請檢查EVT_WDF_REQUEST_COMPLETION_ROUTINE中的 URB 狀態。 Params 參數指向WDF_REQUEST_COMPLETION_PARAMS結構。 若要檢查 USBD 狀態代碼,請檢查 Usb-UsbdStatus> 成員。 如需程式代碼的相關信息,請參閱 USBD_STATUS。
傳輸失敗可能會導致裝置錯誤,例如USBD_STATUS_STALL_PID或USBD_STATUS_BABBLE_DETECTED。 它們也可能是因為主控制器回報的錯誤所造成,例如USBD_STATUS_XACT_ERROR。
步驟 2:判斷裝置是否已連線到埠
在發出重設管道或裝置的任何要求之前,請確定裝置已連線。 您可以呼叫 WdfUsbTargetDeviceIsConnectedSynchronous 方法來判斷裝置的連線狀態。
步驟 3:取消所有暫止傳送至管道
傳送任何重設管道或埠的要求之前,請先取消所有擱置的傳送要求至管道,USB 驅動程式堆疊尚未完成。 您可以透過下列其中一種方式取消要求:
呼叫 WdfIoTargetStop 方法以停止 I/O 目標。
若要停止 I/O 目標,請先呼叫 WdfUsbTargetPipeGetIoTarget 方法,以取得與架構管道對象相關聯的 WDFIOTARGET 句柄。 藉由使用句柄,呼叫 WdfIoTargetStop。 在呼叫中,將動作設定為 WdfIoTargetCancelSentIo (請參閱 WDF_IO_TARGET_SENT_IO_ACTION)**,以指示架構取消 USB 驅動程式堆疊尚未完成的所有要求。 對於已完成的要求,客戶端驅動程序必須等候其完成回呼,才能由架構叫用。
傳送中止管道要求。 您可以呼叫下列其中一種方法來傳送要求:
呼叫 WdfUsbTargetPipeAbortSynchronously 方法。
呼叫是同步的,只有在取消所有擱置的要求之後才會傳回。 WdfUsbTargetPipeAbortSynchronously 會採用選擇性 的 Request 參數。 我們建議您將 WDFREQUEST 句柄傳遞至預先配置的架構要求物件。 參數會啟用使用指定要求對象的架構,而不是驅動程式無法存取的內部要求物件。 此參數值可確保 WdfUsbTargetPipeAbortSynchronously 不會因為記憶體不足而失敗。
呼叫 WdfUsbTargetPipeFormatRequestForAbort 方法來格式化中止管道要求的要求對象,然後呼叫 WdfRequestSend 方法傳送要求。
如果驅動程式以異步方式傳送要求,則必須指定驅動程式所實作的 驅動程式EVT_WDF_REQUEST_COMPLETION_ROUTINE 指標。 若要指定指標,請呼叫 WdfRequestSetCompletionRoutine 方法。
驅動程式可以將WDF_REQUEST_SEND_OPTION_SYNCHRONOUS指定為 WdfRequestSend 中的其中一個要求選項,以同步傳送要求。 如果您以同步方式傳送要求,請改為呼叫 WdfUsbTargetPipeAbortSynchronously 。
步驟 4:重設 USB 管道
重設管道以啟動錯誤復原。 您可以呼叫下列其中一種方法來傳送重設管道要求:
呼叫 WdfUsbTargetPipeResetSynchronously 以同步傳送重設管道要求。
呼叫 WdfUsbTargetPipeFormatRequestForReset 方法來格式化重設管道要求的要求對象,然後呼叫 WdfRequestSend 方法來傳送要求。 這些呼叫類似於中止管道要求的呼叫,如步驟 3 中所述。
注意
在重設管道作業完成之前,請勿傳送任何新的傳輸要求。
重設管道要求會清除裝置和主機控制器硬體中的錯誤狀況。 若要清除裝置錯誤,USB 驅動程式堆疊會使用ENDPOINT_HALT功能選取器,將CLEAR_FEATURE控制要求傳送給裝置。 要求的收件者是與管道相關聯的端點。 如果錯誤狀況發生在不時針管道上,則驅動程式堆疊不會採取任何動作來清除裝置,因為若發生錯誤,系統會自動清除不時點端點。
若要清除主控制器錯誤,驅動程式堆疊會清除管道的 HALT 狀態,並將管道的數據切換重設為 0。
步驟 5:重設 USB 埠
如果重設管道作業無法清除錯誤狀況,且數據傳輸會繼續失敗,請傳送重設埠要求。
取消所有傳輸至裝置。 若要這樣做,請列舉目前組態中的所有管道,並取消針對每個管道排程的擱置要求。
停止裝置的 I/O 目標。
呼叫 WdfUsbTargetDeviceGetIoTarget 方法,以取得與架構目標裝置對象相關聯的 WDFIOTARGET 句柄。 然後,呼叫 WdfIoTargetStop 並指定 WDFIOTARGET 句柄。 在呼叫中,將動作設定為 WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION)。
呼叫 WdfUsbTargetDeviceResetPortSynchronously 方法來傳送重設埠要求。
重設埠作業會導致裝置在USB總線上重新列舉。 USB 驅動程式堆疊會在 列舉之後保留裝置組態。 用戶端驅動程式可以使用先前取得的管道句柄,因為驅動程式堆疊可確保現有的管道控點保持有效。
您無法重設複合裝置的個別功能。 針對複合裝置,當特定函式的用戶端驅動程式傳送重設埠要求時,會重設整個裝置。 如果 USB 裝置維持狀態,重設埠要求可能會影響其他功能的用戶端驅動程式。 因此,客戶端驅動程式在重設埠之前,請務必先嘗試重設管道。
步驟 6:迴圈 USB 埠
迴圈埠作業類似於未插接並插回埠的裝置,但裝置未以電力中斷連線。 裝置已中斷連線,並在軟體中重新連線。 此作業會導致裝置重設和列舉。 因此,PnP 管理員會重建裝置節點。
如果重設埠作業無法清除錯誤狀況,且數據傳輸會繼續失敗,請傳送迴圈埠要求。
取消所有傳輸至裝置。 請確定您取消針對目前組態中每個管道排程的擱置要求(請參閱步驟 3)。
停止裝置的 I/O 目標。
呼叫 WdfUsbTargetDeviceGetIoTarget 方法,以取得與架構目標裝置對象相關聯的 WDFIOTARGET 句柄。 然後,呼叫 WdfIoTargetStop 並指定 WDFIOTARGET 句柄。 在呼叫中,將動作設定為 WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION)。
呼叫下列其中一種方法來傳送迴圈埠要求:
- 呼叫 WdfUsbTargetDeviceCyclePortSynchronously 以同步傳送週期埠要求。
- 呼叫 WdfUsbTargetDeviceFormatRequestForCyclePort 方法,以格式化迴圈埠要求的要求對象,然後呼叫 WdfRequestSend 方法來傳送要求。 這些呼叫類似於中止管道要求的呼叫,如步驟 3 中所述。
用戶端驅動程式只有在週期埠要求完成之後,才能將傳送要求傳送至裝置。 這是因為當 USB 驅動程式堆疊處理循環埠要求時,裝置節點會移除。
迴圈埠要求會導致裝置重新列舉。 USB 驅動程式堆疊會通知 PnP 管理員裝置已中斷連線。 PnP 管理員會卸除與客戶端驅動程式相關聯的裝置堆疊。 驅動程式堆疊會重設裝置、在USB總線上重新列舉它,並通知 PnP 管理員裝置已連線。 然後,PnP 管理員會重建 USB 裝置的裝置堆疊。
由於迴圈埠作業,任何已開啟裝置句柄的應用程式都會取得裝置移除通知(如果應用程式已註冊這類通知)。 回應中,應用程式可能會向用戶回報裝置中斷連線的訊息。 因為它會影響用戶體驗,所以只有在其他復原機制無法解決錯誤狀況時,用戶端驅動程式才應該選擇使用週期埠要求。
類似於重設埠作業(如步驟 6 所述),對於複合裝置,迴圈埠作業會影響整個裝置,而不是裝置的個別功能。