共用方式為


如何傳送 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 裝置或從 USB 裝置接收數據。 在此範例中,讓我們檢查 LeCroy USB 分析器所擷取的一些追蹤。 此範例僅供參考。 這不是 Microsoft 的簽署。

設定交易

主機一律會起始控制傳輸。 這麼做的方式是傳送安裝交易。 此交易包含稱為 安裝令牌 的令牌封包,後面接著 8 位元組的數據封包。 此螢幕快照顯示範例設定交易。

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

在上述追蹤中,主機會藉由傳送安裝令牌封包 #434 來起始 (H \) 控制傳輸。 請注意,PID 會指定指出安裝令牌的SETUP。 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 個字節的方式取決於預設端點可在一筆交易中傳送的數據量。 該資訊包含在數據交易中裝置傳回的裝置描述元中。

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

數據交易

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

以下是數據交易的追蹤。

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

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

回應中,裝置會傳送遵循 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 目標裝置物件。

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

KMDF 用戶端驅動程式

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

UMDF 用戶端驅動程式

UMDF 用戶端驅動程序必須藉由查詢架構目標裝置物件來取得 IWDFUsbTargetDevice 指標。 For more information, see "IPnpCallbackHardware implementation and USB-specific tasks" in Understanding the USB client driver code structure (UMDF).

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

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

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

如果您要撰寫 UMDF 驅動程式,請從 UMDF 範例驅動程式 for OSR USB Fx2 Learning Kit 取得頭檔 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 中定義的協助程式宏和結構。 此標頭隨附於 UMDF 範例驅動程式 for OSR USB Fx2 Learning Kit。

使用此表格來判斷將控制要求傳送至 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_CLASS或 WINUSB_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. 視您以同步或異步方式傳送要求而定,請指定您的傳送選項:

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

  5. 呼叫 WdfUsbTargetDeviceSendControlTransferSynchronouslyWdfUsbTargetDeviceFormatRequestForControlTransfer 來傳送要求。

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

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

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. 將頭檔包含在 UMDF 範例驅動程式 for OSR USB Fx2 Learning Kit 提供的Usb_hw.h。

  2. 宣告 WINUSB_CONTROL_SETUP_PACKET 結構。

  3. 呼叫協助程序宏, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS,以初始化設定封包。

  4. BmRequestToDevice 指定為收件者。

  5. [索引 ] 值中指定 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 函式。