共用方式為


如何傳送 USB 控制件傳輸

本文說明控制項傳輸的結構,以及客戶端驅動程式應如何將控件要求傳送至裝置。

關於預設端點

所有 USB 裝置都必須支援至少一個稱為預設端點的 端點。 以預設端點為目標的任何傳輸稱為 控制傳輸。 控制傳輸的目的是讓主機取得裝置資訊、設定裝置,或執行裝置專屬的控制作業。

讓我們從研究預設端點的這些特性開始。

  • 默認端點的位址為 0。
  • 默認端點是雙向的,也就是說,主機可以將數據傳送至端點,並在一次傳輸中接收數據。
  • 默認端點可在裝置層級使用,且未定義於裝置的任何介面中。
  • 一旦主機與裝置之間建立連線,預設端點就會處於作用中狀態。 即使在選取組態之前,它仍為作用中。
  • 默認端點的封包大小上限取決於裝置的總線速度。 低速,8 個字節;完整和高速,64 個字節;SuperSpeed,512 個字節。

控制件傳輸的配置

因為控制傳輸是高優先順序的傳輸,因此主機會在總線上保留特定數量的頻寬。 針對低速和全速裝置,10% 的頻寬;20% 用於高和超速傳輸裝置。 現在,讓我們看看控件傳輸的配置。

USB 控制件傳輸的圖表。

控制傳輸分為三個交易: 設定交易數據交易狀態交易。 每個交易都包含三種類型的封包:令牌封包、數據封交握封包

某些欄位適用於所有封包。 這些欄位是:

  • 表示封包開頭的同步欄位。
  • 表示封包類型、交易方向,以及在交握封包的情況下,指出交易成功或失敗的封包標識碼(PID)。
  • EOP 欄位表示封包的結尾。

其他欄位取決於封包的類型。

令牌封包

每個設定交易都會以令牌封包開始。 以下是封包的結構。 主機一律會傳送令牌封包。

令牌封包配置的圖表。

PID 值表示令牌封包的類型。 下列為可能的值:

  • 安裝程式:表示控制項傳輸中設定交易的開始。
  • IN:表示主機正在向裝置要求數據(讀取案例)。
  • OUT:指出主機正在將數據傳送至裝置(寫入案例)。
  • SOF:表示框架的開頭。 這種類型的令牌封包包含11位框架編號。 主機會傳送SOF封包。 傳送此封包的頻率取決於總線速度。 針對完整速度,主機每1百萬次傳送封包一次;高速巴士每 125 微秒。

數據封包

緊接在令牌封包之後是包含承載的數據封包。 每個數據封包可以包含的位元組數目取決於預設端點的封包大小上限。 視傳輸方向而定,主機或裝置可以傳送數據封包。

數據封包配置圖表。

交握封包

緊接在數據封包之後是交握封包。 封包的 PID 表示主機或裝置是否收到封包。 根據傳輸的方向,主機或裝置可以傳送交握封包。

交握封包配置的圖表。

您可以使用任何 USB 分析器來查看交易和封包的結構,例如 Beagle、Ellisys、LeCroy USB 通訊協定分析器。 分析器裝置會示範如何透過網路從 USB 裝置傳送或接收數據。 在此範例中,讓我們檢查 LeCroy USB 分析器所擷取的一些追蹤。 此範例僅供資訊使用。 這不是Microsoft背書。

設定交易

主機一律會起始控件傳輸。 它藉由傳送安裝交易來進行。 此交易包含稱為 安裝令牌的令牌 封包,後面接著 8 位元組的數據封包。 此螢幕快照顯示設定交易的範例。

設定交易追蹤的螢幕快照。

在上述追蹤中,主機會藉由傳送安裝令牌封包 #434 來起始控制傳輸(以 H++表示)。 請注意,PID 指定安裝程式,指出安裝令牌。 PID 後面接著裝置位址和端點的位址。 針對控制傳輸,該端點位址一律為 0。

接下來,主機會傳送數據封包 #435。 PID 是 DATA0,且該值用於封包排序(要討論)。 PID 後面接著 8 個字節,其中包含此要求的主要資訊。 這8個字節表示要求類型和裝置將寫入其回應的緩衝區大小。

所有位元組都會以反向順序接收。 如第 9.3 節所述,我們看到這些欄位和值:

欄位 大小 Description
bmRequestType (請參閱 9.3.1 bmRequestType) 1 0x80 資料傳輸方向是從裝置到主機 (D7 是 1)

要求是標準要求 (D6...D5 為 0)

要求的收件者是 DEVICE (D4 是 0)
bRequest (請參閱<9.3.2>一節和表 9-4) 1 0x06 要求類型為GET_DESCRIPTOR。
wValue (請參閱表 9-5) 2 0x0100 要求值表示描述元類型為 DEVICE。
wIndex (請參閱 9.3.4 節) 2 0x0000 方向是從主機到裝置 (D7 是 1)

端點編號為 0。
wLength (請參閱第 9.3.5 節) 2 0x0012 要求是擷取 18 個字節。

因此,我們可以得出結論,在此控件(讀取)傳輸中,主機會傳送要求來擷取裝置描述元,並將 18 個字節指定為傳輸長度來保存該描述元。 裝置傳送這 18 個字節的方式取決於預設端點可以在單一交易中傳送的數據量。 該資訊包含在數據交易中裝置傳回的裝置描述元中。

回應中,裝置會傳送交握封包 (#436,以 D++表示)。 請注意,PID 值為 ACK (ACK 封包)。 這表示裝置已認可交易。

數據交易

現在,讓我們看看裝置在回應要求時傳回的內容。 實際數據會在數據交易中傳輸。

以下是數據交易的追蹤。

顯示範例數據交易追蹤的螢幕快照。

收到 ACK 封包時,主機會起始數據交易。 若要起始交易,它會以 IN 的形式傳送具有方向的令牌封包 (#450) (稱為 IN Token)。

回應中,裝置會傳送遵循 IN 令牌的數據封包 (#451)。 此數據封包包含實際的裝置描述元。 第一個字節表示裝置描述元的長度,18 個字節(0x12)。 此數據封包中的最後一個字節表示預設端點所支援的封包大小上限。 在此情況下,我們看到裝置可以一次透過其預設端點傳送8個字節。

注意

默認端點的封包大小上限取決於裝置的速度。 高速裝置的預設端點為64個字節;低速裝置為8個字節。

主機會藉由將 ACK 封包 (#452) 傳送至裝置來確認數據交易。

讓我們計算傳回的數據量。 在 安裝交易中數據封包 (#435) 的 wLength 字段中,主機要求 18 個字節。 在數據交易中,我們看到只會從裝置接收前8個字節的裝置描述元。 那麼,主機如何接收儲存在其餘 10 個字節中的資訊? 裝置會在兩個交易中執行此動作:8 個字節,然後是最後 2 個字節。

現在主機知道預設端點的封包大小上限,主機會起始新的數據交易,並根據封包大小要求下一個部分。

以下是下一個數據交易:

顯示新數據交易追蹤的螢幕快照。

主機會藉由傳送 IN 令牌 (#463) 並從裝置要求接下來的 8 個字節來起始上述數據交易。 裝置會以包含裝置描述元接下來 8 個字節的數據封包 (#464) 回應。

收到8個字節時,主機會將ACK封包 (#465) 傳送至裝置。

接下來,主機會要求另一個數據交易中的最後 2 個字節,如下所示:

此螢幕快照顯示主機要求最後 2 個字節之新範例數據交易的追蹤。

因此,我們看到,若要將 18 個字節從裝置傳輸至主機,主機會持續追蹤傳輸並起始三個數據交易的位元元組數目(8+8+2)。

注意

請注意數據交易 19、 23、 26 中的數據封包 PID。 DATA0 與 DATA1 之間的 PID 替代專案。 此序列稱為數據切換。 如果有多個數據交易,則會使用數據切換來驗證封包順序。 此方法可確保數據封包不會重複或遺失。

藉由將合併的數據封包對應至裝置描述元的結構(請參閱表 9-8),我們會看到這些字段和值:

欄位 大小 Description
bLength 1 0x12 裝置描述元的長度,也就是 18 個字節。
bDescriptorType 1 0x01 描述項類型是裝置。
bcdUSB 2 0x0100 規格版本號碼為 1.00。
bDeviceClass 1 0x00 裝置類別為 0。 組態中的每個介面都有類別資訊。
bDeviceSubClass 1 0x00 子類別是 0,因為裝置類別是 0。
bProtocol 1 0x00 通訊協定為 0。 此裝置不會使用任何類別特定的通訊協定。
bMaxPacketSize0 1 0x08 端點的封包大小上限為8個字節。
idVendor 2 0x0562 Telex Communications。
idProduct 2 0x0002 USB 麥克風。
bcdDevice 2 0x0100 指出裝置版本號碼。
iManufacturer 1 0x01 製造商字串。
iProduct 1 0x02 產品字串。
iSerialNumber 1 0x03 序號。
bNumConfigurations 1 0x01 組態數目。

藉由檢查這些值,我們有一些關於裝置的初步資訊。 裝置是低速 USB 麥克風。 默認端點的封包大小上限為8個字節。 裝置支援一個設定。

狀態交易

最後,主機會藉由起始最後一筆交易:狀態交易來完成控制傳輸。

範例數據交易追蹤的螢幕快照。

主機會使用 OUT 令牌封包來啟動交易(#481)。 此封包的目的是要確認裝置是否已傳送所有要求的數據。 此狀態交易中沒有任何數據封包傳送。 裝置會以 ACK 封包回應。 如果發生錯誤,PID 可能是 NAK 或 STALL。

驅動程式模型

必要條件

在客戶端驅動程式可以列舉管道之前,請確定符合下列需求:

  • 用戶端驅動程序必須已建立架構 USB 目標裝置物件。

    如果您使用visual Studio Professional 2012 Microsoft提供的USB樣本,範本程式代碼會執行這些工作。 範本程式代碼會取得目標裝置物件的句柄,並儲存在裝置內容中。

KMDF 用戶端驅動程式

KMDF 用戶端驅動程式必須藉由呼叫 WdfUsbTargetDeviceCreateWithParameters 方法來取得 WDFUSBDEVICE 句柄。 如需詳細資訊,請參閱瞭解USB用戶端驅動程式程式代碼結構(KMDF)中的

UMDF 用戶端驅動程式

UMDF 用戶端驅動程序必須藉由查詢架構目標裝置物件來取得 IWDFUsbTargetDevice 指標。 如需詳細資訊,請參閱瞭解USB用戶端驅動程式程式代碼結構(UMDF)中的<IPnpCallbackHardware 實作和USB特定工作>。

控件傳輸最重要的層面是適當地格式化設定令牌。 傳送要求之前,請先收集這組資訊:

  • 要求的方向:主機到裝置或要裝載的裝置。
  • 要求的收件者:裝置、介面、連接點或其他。
  • 要求類別:標準、類別或廠商。
  • 要求的類型,例如GET_DESCRIPTPOR要求。 如需詳細資訊,請參閱USB規格中的9.5節。
  • wValuewIndex 值。 這些值取決於要求的類型。

您可以從官方 USB 規格取得所有這些資訊。

如果您要撰寫 UMDF 驅動程式,請從適用於 OSR USB Fx2 學習工具包的 UMDF 範例驅動程式取得頭檔Usb_hw.h。 此標頭檔包含有用的巨集和結構,可用來格式化控件傳輸的設定封包。

所有UMDF驅動程式都必須與內核模式驅動程序通訊,才能從裝置傳送和接收數據。 針對 USB UMDF 驅動程式,內核模式驅動程式一律是Microsoft提供的驅動程式 WinUSB (Winusb.sys)。

每當 UMDF 驅動程式提出 USB 驅動程式堆疊的要求時,Windows I/O 管理員就會將要求傳送至 WinUSB。 收到要求之後,WinUSB 會處理要求,或將它轉送至USB驅動程式堆疊。

傳送控制傳輸要求的Microsoft定義方法

主機上的 USB 用戶端驅動程式會起始大部分的控制要求,以取得裝置的相關信息、設定裝置或傳送廠商控制命令。 所有這些要求都可以分類為:

  • 標準要求 定義於 USB 規格中。 傳送標準要求的目的是取得裝置、其組態、介面和端點的相關信息。 每個要求的收件者取決於要求的類型。 收件者可以是裝置、介面或連接點。

    注意

    任何控制傳輸的目標一律是預設端點。 收件者是裝置的實體,其資訊(描述項、狀態等等)主機有興趣。

    要求可以進一步分類為:組態要求、功能要求和狀態要求。

    • 設定要求 會傳送至從裝置取得資訊,讓主機可以進行設定,例如GET_DESCRIPTOR要求。 這些要求也可以是主機傳送以在裝置中設定特定組態或替代設定的寫入要求。
    • 用戶端驅動程式會傳送功能要求 ,以啟用或停用裝置、介面或端點所支援的特定布爾裝置設定。
    • 狀態要求 可讓主機取得或設定裝置、連接點或介面的USB定義狀態位。

    如需詳細資訊,請參閱USB規格2.0版中的第9.4節。 標準要求類型會定義頭檔Usbspec.h。

  • 類別要求 是由特定裝置類別規格所定義。

  • 廠商要求 是由廠商提供,取決於裝置所支援的要求。

Microsoft提供的 USB 堆疊會處理與裝置的所有通訊協定通訊,如上述追蹤所示。 驅動程式會公開設備驅動器介面 (DIS),讓客戶端驅動程式以多種方式傳送控制傳輸。 如果您的用戶端驅動程式是 Windows Driver Foundation (WDF) 驅動程式,它可以直接呼叫例程來傳送常見的控制要求類型。 WDF 支援 KMDF 和 UMDF 的內部控制傳輸。

某些類型的控制要求不會透過WDF公開。 針對這些要求,客戶端驅動程式可以使用WDF混合式模型。 此模型可讓客戶端驅動程式建置及格式化 WDM URB 樣式的要求,然後使用 WDF 架構物件傳送這些要求。 混合式模型僅適用於內核模式驅動程式。

針對 UMDF 驅動程式:

使用 usb_hw.h 中定義的協助程式巨集和結構。 此標頭隨附於適用於OSR USB Fx2 學習套件的UMDF範例驅動程式。

使用此表格來判斷將控制要求傳送至 USB 驅動程式堆疊的最佳方式。 如果您無法檢視此資料表,請參閱本文中的表格。

如果您要將控制項要求傳送至... 對於 KMDF 驅動程式... 針對 UMDF 驅動程式... 針對 WDM 驅動程式,建置 URB 結構 (協助程式例程)
CLEAR_FEATURE:停用裝置中的特定功能設定、其組態、介面和端點。 請參閱 USB 規格中的 9.4.1 節。
  1. 宣告安裝封包。 請參閱WDF_USB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE,以初始化設定封包。
  3. 指定WDF_USB_BMREQUEST_RECIPIENT定義的收件者值。
  4. 指定功能選取器 (wValue)。 請參閱 Usbspec.h 中的USB_FEATURE_XXX常數。 另請參閱 USB 規格中的表格 9-6。
  5. 將 SetFeature 設定FALSE
  6. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。
  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 usb_hw.h 中定義的 helper 巨集 WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE,以初始化設定封包。
  3. 指定WINUSB_BMREQUEST_RECIPIENT定義的收件者值。
  4. 指定功能選取器 (wValue)。 請參閱 Usbspec.h 中的USB_FEATURE_XXX 常數。 另請參閱 USB 規格中的表格 9-6。
  5. 將 SetFeature 設定FALSE
  6. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  7. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
_URB_CONTROL_FEATURE_REQUEST

UsbBuildFeatureRequest

URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE

URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE

URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT

URB_FUNCTION_CLEAR_FEATURE_TO_OTHER
GET_CONFIGURATION:取得目前的USB設定。 請參閱 USB 規格中的 9.4.2 節。 KMDF 預設會選取第一個設定。 若要擷取裝置定義的組態編號:

  1. 將WDF_USB_CONTROL_SETUP_PACKET格式化,並將其 bRequest 成員設定USB_REQUEST_GET_CONFIGURATION
  2. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。
UMDF 預設會選取第一個組態。 若要擷取裝置定義的組態編號:

  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 usb_hw.h 中定義的協助程式 巨集WINUSB_CONTROL_SETUP_PACKET_INIT,以初始化設定封包。
  3. 將 BmRequestToDevice 指定為方向、BmRequestToDevice 指定為收件者,並將USB_REQUEST_GET_CONFIGURATION作為要求。
  4. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  5. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
  6. 接收傳輸緩衝區中的組態號碼。 呼叫 IWDFMemory 方法來存取該緩衝區。
_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION
GET_DESCRIPTOR:取得裝置、組態、介面和端點描述項。 請參閱 USB 規格中的 9.4.3 節。

如需詳細資訊,請參閱 USB描述元
呼叫下列方法:

呼叫下列方法:

_URB_CONTROL_DESCRIPTOR_REQUEST

UsbBuildGetDescriptorRequest

URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE

URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT

URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE
GET_INTERFACE:取得介面目前的替代設定。 請參閱 USB 規格中的 9.4.4 節。

  1. 呼叫 WdfUsbTargetDeviceGetInterface 方法,以取得目標介面物件的 WDFUSBINTERFACE 句柄。
  2. 呼叫 WdfUsbInterfaceGetConfiguredSettingIndex 方法。
  1. 取得目標介面物件的IWDFUsbInterface指標。
  2. 呼叫 IWDFUsbInterface::GetConfiguredSettingIndex 方法。
_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE
GET_STATUS:從裝置、端點或介面取得狀態位。 請參閱 9.4.5 節。 在USB規格中。
  1. 宣告安裝封包。 請參閱WDF_USB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS,以初始化設定封包。
  3. 指定WDF_USB_BMREQUEST_RECIPIENT定義的收件者值。
  4. 指定您想要取得的狀態:裝置、介面或連接點 (wIndex)。
  5. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。
  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 usb_hw.h 中定義的 helper 巨集 WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS,以初始化設定封包。
  3. 指定WINUSB_BMREQUEST_RECIPIENT定義的收件者值。
  4. 指定您想要取得的狀態:裝置、介面或連接點 (wIndex)。
  5. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  6. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
  7. 在傳輸緩衝區中接收狀態值。 呼叫 IWDFMemory 方法來存取該緩衝區。
  8. 若要判斷狀態是否表示自我供電、遠端喚醒,請使用 WINUSB_DEVICE_TRAITS 列舉中定義的值:
_URB_CONTROL_GET_STATUS_REQUEST

UsbBuildGetStatusRequest

URB_FUNCTION_GET_STATUS_FROM_DEVICE

URB_FUNCTION_GET_STATUS_FROM_INTERFACE

URB_FUNCTION_GET_STATUS_FROM_ENDPOINT

URB_FUNCTION_GET_STATUS_FROM_OTHER。
SET_ADDRESS:請參閱USB規格中的9.4.6節。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。
SET_CONFIGURATION:設定組態。 請參閱 USB 規格中的 9.4.7 節。

如需詳細資訊,請參閱 如何選取USB裝置的設定。
根據預設,KMDF 會選取每個介面中的預設組態和第一個替代設定。 用戶端驅動程式可以呼叫 WdfUsbTargetDeviceSelectConfigType 方法來變更預設組態,並將 WdfUsbTargetDeviceSelectConfigTypeUrb 指定為要求選項。 然後,您必須為此要求格式化 URB,並將其提交至 USB 驅動程式堆疊。 根據預設,UMDF 會選取每個介面中的預設組態和第一個替代設定。 用戶端驅動程式無法變更設定。 _URB_SELECT_CONFIGURATION

USBD_SelectConfigUrbAllocateAndBuild

URB_FUNCTION_SELECT_CONFIGURATION
SET_DESCRIPTOR:更新現有的裝置、組態或字串描述元。 請參閱 USB 規格中的 9.4.8 節。

此要求不常使用。 不過,USB 驅動程式堆疊會接受來自客戶端驅動程式的這類要求。
  1. 配置並建置要求的 URB
  2. 在 _URB_CONTROL_DESCRIPTOR_REQUEST結構中指定傳輸資訊。
  3. 呼叫 WdfUsbTargetDeviceFormatRequestForUrbWdfUsbTargetDeviceSendUrbSynchronously 來傳送要求。
  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 根據 USB 規格指定傳輸資訊。
  3. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  4. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
_URB_CONTROL_DESCRIPTOR_REQUEST

URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE

URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT

URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE
SET_FEATURE:在裝置、其組態、介面和端點中啟用特定功能設定。 請參閱 USB 規格中的 9.4.9 節。
  1. 宣告安裝封包。 請參閱WDF_USB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE,以初始化設定封包。
  3. 指定WDF_USB_BMREQUEST_RECIPIENT中定義的收件者值(裝置、介面、連接點)。
  4. 指定功能選取器 (wValue)。 請參閱 Usbspec.h 中的USB_FEATURE_XXX常數。 另請參閱 USB 規格中的表格 9-6。
  5. 將 SetFeature 設定TRUE
  6. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。
  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 usb_hw.h 中定義的 helper 巨集 WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE,以初始化設定封包。
  3. 指定WINUSB_BMREQUEST_RECIPIENT定義的收件者值。
  4. 指定功能選取器 (wValue)。 請參閱 Usbspec.h 中的USB_FEATURE_XXX 常數。 另請參閱 USB 規格中的表格 9-6。
  5. 將 SetFeature 設定TRUE
  6. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  7. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
_URB_CONTROL_FEATURE_REQUEST

UsbBuildFeatureRequest

URB_FUNCTION_SET_FEATURE_TO_DEVICE

URB_FUNCTION_SET_FEATURE_TO_INTERFACE

URB_FUNCTION_SET_FEATURE_TO_ENDPOINT

URB_FUNCTION_SET_FEATURE_TO_OTHER
SET_INTERFACE:變更介面中的替代設定。 請參閱 USB 規格中的 9.4.9 節。

如需詳細資訊,請參閱 如何在USB介面中選取替代設定。
WdfUsbTargetDeviceSelectConfig
  1. 取得目標介面物件的 WDFUSBINTERFACE 句柄。
  2. 呼叫 WdfUsbInterfaceSelectSetting 方法。
  1. 取得目標介面物件的IWDFUsbInterface指標。
  2. 呼叫 IWDFUsbInterface::SelectSetting 方法。
_URB_SELECT_INTERFACE

USBD_SelectInterfaceUrbAllocateAndBuild

URB_FUNCTION_SELECT_INTERFACE
SYNC_FRAME:設定和取得和端點的同步處理框架編號。 請參閱 USB 規格中的 9.4.10 節。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。 此要求是由 USB 驅動程式堆疊處理;用戶端驅動程式無法執行這項作業。
針對裝置類別特定的要求和廠商命令。
  1. 宣告安裝封包。 請參閱WDF_USB_CONTROL_SETUP_PACKET結構。
  2. 呼叫廠商命令的WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS特定要求或WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR,以初始化設定封包。
  3. 指定WDF_USB_BMREQUEST_RECIPIENT中定義的收件者值(裝置、介面、連接點)。
  4. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。
  1. 宣告安裝封包。 請參閱 usb_hw.h 中宣告的WINUSB_CONTROL_SETUP_PACKET結構。
  2. 呼叫 usb_hw.h 中定義的協助程式 巨集WINUSB_CONTROL_SETUP_PACKET_INIT_CLASSWINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR,以初始化設定封包。
  3. 指定方向(請參閱WINUSB_BMREQUEST_DIRECTION列舉)、收件者(請參閱WINUSB_BMREQUEST_RECIPIENT列舉),以及要求,如 類別或硬體規格中所述。
  4. 藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區來建置要求。
  5. 呼叫 IWDFIoRequest::Send 方法來傳送要求。
  6. 從傳輸緩衝區中的裝置接收資訊。 呼叫 IWDFMemory 方法來存取該緩衝區。
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST

UsbBuildVendorRequest

URB_FUNCTION_VENDOR_DEVICE

URB_FUNCTION_VENDOR_INTERFACE

URB_FUNCTION_VENDOR_ENDPOINT

URB_FUNCTION_VENDOR_OTHER

URB_FUNCTION_CLASS_DEVICE

URB_FUNCTION_CLASS_INTERFACE

URB_FUNCTION_CLASS_ENDPOINT

URB_FUNCTION_CLASS_OTHER

如何傳送廠商命令的控制傳輸 - KMDF

此程式示範客戶端驅動程式如何傳送控件傳輸。 在此範例中,客戶端驅動程式會傳送廠商命令,從裝置擷取韌體版本。

  1. 宣告廠商命令的常數。 研究硬體規格,並判斷您想要使用的廠商命令。

  2. 宣告WDF_MEMORY_DESCRIPTOR結構,並藉由呼叫WDF_MEMORY_DESCRIPTOR_INIT_BUFFER巨集加以初始化。 此結構會在 USB 驅動程式完成要求之後收到來自裝置的回應。

  3. 視您以同步或異步方式傳送要求而定,請指定您的傳送選項:

    • 如果您藉由呼叫 WdfUsbTargetDeviceSendControlTransferSynchronously 同步傳送要求,請指定逾時值。 此值很重要,因為若沒有逾時,您可以無限期地封鎖線程。

      為此,請宣告 WDF_REQUEST_SEND_OPTIONS 結構,並藉由呼叫 WDF_REQUEST_SEND_OPTIONS_INIT 巨集 加以初始化。 將選項指定為 WDF_REQUEST_SEND_OPTION_TIMEOUT

      接下來,呼叫WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT巨集設定逾時值。

    • 如果您要以異步方式傳送要求,請實作完成例程。 釋放完成例程中的所有已配置資源。

  4. 宣告WDF_USB_CONTROL_SETUP_PACKET結構,以包含安裝令牌並格式化結構。 若要這樣做,請呼叫 WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 巨集來格式化設定封包。 在呼叫中,指定要求的方向、收件者、傳送的要求選項(在步驟 3 中初始化),以及廠商命令的常數。

  5. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。

  6. 檢查架構傳回的NTSTATUS值,並檢查收到的值。

此程式代碼範例會將控件傳輸要求傳送至USB裝置,以擷取其韌體版本。 要求會同步傳送,而用戶端驅動程式會指定 5 秒的相對逾時值(以 100 奈秒為單位)。 驅動程式會將收到的回應儲存在驅動程式定義的裝置內容中。

enum {
    USBFX2_GET_FIRMWARE_VERSION = 0x1,
....

} USBFX2_VENDOR_COMMANDS; 

#define WDF_TIMEOUT_TO_SEC              ((LONGLONG) 1 * 10 * 1000 * 1000)  // defined in wdfcore.h

const __declspec(selectany) LONGLONG
            DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; 


typedef struct _DEVICE_CONTEXT
{

    ...
       union {
        USHORT      VersionAsUshort;
        struct {
            BYTE Minor;
            BYTE Major;
        } Version;
    } Firmware; // Firmware version.

} DEVICE_CONTEXT, *PDEVICE_CONTEXT;


__drv_requiresIRQL(PASSIVE_LEVEL)
VOID  GetFirmwareVersion(
    __in PDEVICE_CONTEXT DeviceContext
)
{
    NTSTATUS                        status;
    WDF_USB_CONTROL_SETUP_PACKET    controlSetupPacket;
    WDF_REQUEST_SEND_OPTIONS        sendOptions;
    USHORT                          firmwareVersion;
    WDF_MEMORY_DESCRIPTOR           memoryDescriptor;

    PAGED_CODE();

    firmwareVersion = 0;

    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));

    WDF_REQUEST_SEND_OPTIONS_INIT(
                                  &sendOptions,
                                  WDF_REQUEST_SEND_OPTION_TIMEOUT
                                  );

    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
                                         &sendOptions,
                                         DEFAULT_CONTROL_TRANSFER_TIMEOUT
                                         );

    WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
                                        BmRequestDeviceToHost,       // Direction of the request
                                        BmRequestToDevice,           // Recipient
                                        USBFX2_GET_FIRMWARE_VERSION, // Vendor command
                                        0,                           // Value
                                        0);                          // Index 

    status = WdfUsbTargetDeviceSendControlTransferSynchronously(
                                        DeviceContext->UsbDevice,
                                        WDF_NO_HANDLE,               // Optional WDFREQUEST
                                        &sendOptions,
                                        &controlSetupPacket,
                                        &memoryDescriptor,           // MemoryDescriptor
                                        NULL);                       // BytesTransferred 

    if (!NT_SUCCESS(status)) 
    {
        KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_ERROR,
                    DBG_RUN,
                    "Device %d: Failed to get device firmware version 0x%x\n",
                    DeviceContext->DeviceNumber,
                    status);
    }
    else 
    {
        DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_INFORMATION,
                    DBG_RUN,
                    "Device %d: Get device firmware version : 0x%x\n",
                    DeviceContext->DeviceNumber,
                    firmwareVersion);
    }

    return;
}

如何傳送GET_STATUS的控件傳輸 - UMDF

此程式示範客戶端驅動程式如何傳送GET_STATUS命令的控制項傳輸。 要求的收件者是裝置,而要求會以位 D1-D0 取得資訊。 如需詳細資訊,請參閱USB規格中的圖9-4。

  1. 將頭檔Usb_hw.h 包含在適用於 OSR USB Fx2 學習套件的 UMDF 範例驅動程式中。

  2. 宣告WINUSB_CONTROL_SETUP_PACKET結構。

  3. 呼叫協助程式巨集,WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS初始化設定封包。

  4. 將 BmRequestToDevice 指定為收件者。

  5. 在 Index 值中指定 0。

  6. 呼叫 Helper 方法 SendControlTransferSynchronously 以同步傳送要求。

    協助程式方法會藉由呼叫 IWDFUsbTargetDevice::FormatRequestForControlTransfer 方法,將初始化的設定封包與架構要求物件建立關聯,以及傳輸緩衝區。 接著,協助程式方法會呼叫 IWDFIoRequest::Send 方法來傳送要求。 方法傳回之後,檢查傳回的值。

  7. 若要判斷狀態是否表示自我供電、遠端喚醒,請使用WINUSB_DEVICE_TRAITS列舉中定義的這些值:

此程式代碼範例會將控件傳輸要求傳送至取得裝置的狀態。 此範例會呼叫名為 SendControlTransferSynchronously 的協助程式方法,以同步傳送要求。

HRESULT
CDevice::GetDeviceStatus ()
{

    HRESULT hr = S_OK;

    USHORT deviceStatus;
    ULONG bytesTransferred;

    TraceEvents(TRACE_LEVEL_INFORMATION,
                DRIVER_ALL_INFO,
                "%!FUNC!: entry");

    // Setup the control packet.

    WINUSB_CONTROL_SETUP_PACKET setupPacket;

    WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
                                      &setupPacket,
                                      BmRequestToDevice,
                                      0);

    hr = SendControlTransferSynchronously(
                 &(setupPacket.WinUsb),
                 & deviceStatus,
                 sizeof(USHORT),
                 &bytesReturned
                );

     if (SUCCEEDED(hr))
    {
        if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
        {
             m_Self_Powered = true;
        } 
        if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
        {
             m_remote_wake-enabled = true;
        }
    }

    return hr;
 }

下列程式代碼範例顯示名為 SendControlTransferSynchronously 的協助程式方法實作。 這個方法會同步傳送要求。

HRESULT
CDevice::SendControlTransferSynchronously(
    _In_ PWINUSB_SETUP_PACKET SetupPacket,
    _Inout_ PBYTE Buffer,
    _In_ ULONG BufferLength,
    _Out_ PULONG LengthTransferred
    )
{
    HRESULT hr = S_OK;
    IWDFIoRequest *pWdfRequest = NULL;
    IWDFDriver * FxDriver = NULL;
    IWDFMemory * FxMemory = NULL;
    IWDFRequestCompletionParams * FxComplParams = NULL;
    IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;

    *LengthTransferred = 0;

    hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
                                    NULL, //pParentObject
                                    &pWdfRequest);

    if (SUCCEEDED(hr))
    {
        m_FxDevice->GetDriver(&FxDriver);

        hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
                                                    BufferLength,
                                                    NULL,        //pCallbackInterface
                                                    pWdfRequest, //pParetObject
                                                    &FxMemory );
    }

    if (SUCCEEDED(hr))
    {
        hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
                                                                   SetupPacket,
                                                                   FxMemory,
                                                                   NULL); //TransferOffset
    }

    if (SUCCEEDED(hr))
    {
        hr = pWdfRequest->Send( m_pIUsbTargetDevice,
                                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
                                0); //Timeout
    }

    if (SUCCEEDED(hr))
    {
        pWdfRequest->GetCompletionParams(&FxComplParams);

        hr = FxComplParams->GetCompletionStatus();
    }

    if (SUCCEEDED(hr))
    {
        HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
        WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));

        WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
                            FxUsbComplParams->GetCompletedUsbRequestType() );

        FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
                                                             LengthTransferred,
                                                             NULL,
                                                             NULL );
    }

    SAFE_RELEASE(FxUsbComplParams);
    SAFE_RELEASE(FxComplParams);
    SAFE_RELEASE(FxMemory);

    pWdfRequest->DeleteWdfObject(); 
    SAFE_RELEASE(pWdfRequest);

    SAFE_RELEASE(FxDriver);

    return hr;
}

如果您使用 Winusb.sys 作為裝置的函式驅動程式,您可以從應用程式傳送控制傳輸。 若要格式化 WinUSB 中的設定封包,請使用本文表格中所述的 UMDF 協助程式巨集和結構。 若要傳送要求,請呼叫 WinUsb_ControlTransfer 函式。