---
# System prepended metadata

title: USB 裝置電源管理全覽：BOS、MS OS 2.0 與自動 Suspend

---

@[toc]
## 概要
` 提示：USB Descriptor、STM MCU`
本文以 Descriptor 層級實作為核心（Device/Configuration/BOS + Microsoft OS 2.0），說明如何讓裝置在使用 Windows/Linux 內建類別驅動（如 HID/WinUSB）時，也能安全、可預期地進入 USB Suspend/選擇性省電，並視需要啟用/禁止遠端喚醒。
### Microsoft OS 2.0參考資料
[官方網址](https://learn.microsoft.com/zh-tw/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification)
[官方文檔](https://download.microsoft.com/download/3/5/6/3563ED4A-F318-4B66-A181-AB1D8F6FD42D/MS_OS_2_0_desc.docx)
## 整體架構流程
` 提示：BOS 是入口（宣告能力），MS OS 2.0 描述符才是「告訴 Windows 要建立哪些登錄屬性」的清單。`
 1. 枚舉：主機讀取 Device/Configuration/Interface/Endpoint。
 2. BOS：若啟用 `USBD_CLASS_BOS_ENABLED=1`，主機會再取 BOS Descriptor。
 3. MS OS 2.0 要求：Windows 依 BOS 之 Platform Capability，透過 Vendor Control Request 讀取 `MS_OS_20_DESCRIPTOR_SET`。
 4. Registry 層設定：若 `MS_OS_20_DESCRIPTOR_SET` 內含 Registry Property Descriptor（如 `SelectiveSuspendEnabled=1`、`EnhancedPowerManagementEnabled=1`），Windows 自動建立/更新登錄值。
 5. 省電執行：在無 I/O 的情況下，主機依協議/登錄策略讓裝置進入 USB Suspend（總線無活動 ≥3ms） 或對應的 D-State（對 OS 而言）。
### 示意圖
` 提示：[]屬於USB Device動作。沒有[]屬於電腦Host端的動作`
```
[Device Descriptor] → [Configuration] → [Interface]
        ↓
[BOS Descriptor]
        ↓
[Platform Capability (MS OS 2.0 GUID)]
        ↓
 Windows 傳送 Vendor Request → 要 MS_OS_20_DESCRIPTOR_SET
        ↓
[裝置回傳 Registry Property Descriptor]
        ↓
 Windows 自動建立/更新機碼
```

## 技術名詞解釋
 - Suspend(USB 層)：USB 匯流排無活動達閾值(USB2 約≥3ms)後，裝置進入低功耗。USB需降至 ≤2.5mA(總線供電時)。
 - Remote Wakeup：裝置可向主機發出喚醒訊號；是否允許由 Configuration Descriptor 的 bmAttributes bit5 與主機 `SET_FEATURE/CLEAR_FEATURE(REMOTE_WAKEUP)` 決定。
 - BOS (Binary Object Store)：集中宣告裝置「進階能力」的容器(如 USB 2.0 Extension、SuperSpeed、Platform Capability)。
 - USB 2.0 Extension(LPM)：提供 Link Power Management 能力，縮短進入低功耗的延遲、提升相容性與效率。
 - MS OS 2.0 Descriptor：Microsoft 定義的描述符集合。Windows 讀取後可自動建立 Registry 屬性(如 `SelectiveSuspendEnabled`、`EnhancedPowerManagementEnabled`、`DeviceInterfaceGUIDs`)。
 - D-State(OS 抽象)：作業系統對裝置電源狀態的抽象(D0/D2/D3hot 等)，與 USB 總線層 Suspend 互相關聯但非一個一個對應。
## 技術細節
#### 1.Configuration Descriptor 關鍵欄位
在USBD_FS_BOSDesc中提供Microsoft OS 2.0 Platform Capability Descriptor Header
![BOSDesc增加MOS20](https://i.postimg.cc/Ls1Fq2gt/2025-08-27-140431.png)
#### 2.BOS 與兩個常見開關
##### 1️⃣ `USBD_LPM_ENABLED`
- **LPM = Link Power Management**    
- 這是 **USB 2.0 Extension** 的一部分（屬於 BOS 裡的一個 Capability）。    
- 當你要在 BOS 裡放 **USB 2.0 Extension Descriptor**（例如支援 LPM、Remote Wakeup 增強），就必須把 `USBD_LPM_ENABLED = 1`。    
- 如果沒開，裝置的 BOS 可能會少掉 LPM 相關 Capability，導致 Windows 判斷「裝置不支援 LPM」。    
```
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES 1U
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION 1U
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ 2048U
/*---------- -----------*/
#define USBD_DEBUG_LEVEL 0U
/*---------- -----------*/
#define USBD_LPM_ENABLED 1U
/*---------- -----------*/
#define USBD_SELF_POWERED 1U
/*---------- -----------*/
```
##### 2️⃣ `USBD_CLASS_BOS_ENABLED`
- **BOS = Binary Object Store**    
- 這是一個 USB 2.1 引入的描述符，用來集中宣告裝置的「進階能力」，例如：USB 2.0 Extension（LPM）、USB 3.0 SuperSpeed、Platform Capability (MS OS 2.0 / WebUSB)。    
- `USBD_CLASS_BOS_ENABLED = 1` 代表韌體會在主機請求 BOS 描述符時回應正確的 BOS 結構。    
- 如果沒開，裝置就完全不會回 BOS，主機也無法得知裝置的進階能力，像 MS OS 2.0 或 WebUSB 等功能就不會被觸發。
#### 3.MS OS 2.0 描述符
usbd_msos20.h內容
```
#ifndef __USBD_MSOS20_H__
#define __USBD_MSOS20_H__

#include "usbd_def.h"

extern const uint8_t MS_OS_20_DESCRIPTOR_SET[];
extern const uint16_t MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH;

#endif // __USBD_MSOS20_H__
```
usbd_msos20.c內容
```
//usbd_msos20.c
#include "usbd_def.h"
#include "usbd_msos20.h"

__ALIGN_BEGIN const uint8_t MS_OS_20_DESCRIPTOR_SET[] __ALIGN_END =
{
    //--------------------------------------------
    // MS OS 2.0 Set Header Descriptor (10 bytes)
    //--------------------------------------------
    0x0A, 0x00,                         // wLength - 10 bytes
    0x00, 0x00,                         // wDescriptorType = MS_OS_20_SET_HEADER (0)
    0x00, 0x00, 0x03, 0x06,             // dwWindowsVersion = 0x06030000 (Windows 8.1+)
    0x48, 0x00,                         // wTotalLength = 72 bytes

    //
    // Microsoft OS 2.0 Registry Value Feature Descriptor
    //
    0x3E, 0x00,             // wLength - 62 bytes
    0x04, 0x00,             // wDescriptorType – 4 for Registry Property
    0x04, 0x00,             // wPropertyDataType - 4 for REG_DWORD
    0x30, 0x00,              // wPropertyNameLength – 48 bytes
    0x53, 0x00, 0x65, 0x00, // Property Name - “SelectiveSuspendEnabled”
    0x6C, 0x00, 0x65, 0x00,
    0x63, 0x00, 0x74, 0x00,
    0x69, 0x00, 0x76, 0x00,
    0x65, 0x00, 0x53, 0x00,
    0x75, 0x00, 0x73, 0x00,
    0x70, 0x00, 0x65, 0x00,
    0x6E, 0x00, 0x64, 0x00,
    0x45, 0x00, 0x6E, 0x00,
    0x61, 0x00, 0x62, 0x00,
    0x6C, 0x00, 0x65, 0x00,
    0x64, 0x00, 0x00, 0x00,
    0x04, 0x00,             // wPropertyDataLength – 4 bytes
    0x01, 0x00, 0x00, 0x00  // PropertyData - 0x00000001
};
const uint16_t MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH = sizeof(MS_OS_20_DESCRIPTOR_SET);
```
#### 4.在Setup中攔截0x20
快速測試的作法，建議還是在`HAL_PCD_SetupStageCallback()`中進行更完整的實作。
![快速實作](https://i.postimg.cc/3x6HJdNr/2025-08-27-140332.png)

```
extern USBD_HandleTypeDef hUsbDeviceFS;
int8_t USBD_CUSTOM_HID_VendorRequest(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
    USBD_CtlSendData(&hUsbDeviceFS, (uint8_t*)MS_OS_20_DESCRIPTOR_SET, MS_OS_20_DESCRIPTOR_SET_TOTAL_LENGTH);
    return (USBD_OK);
}
```
### 技術細節改善建議
#### 當前作法
在 `HAL_PCD_SetupStageCallback()` 只攔截「標準 + 裝置收件」的 `SET_FEATURE/CLEAR_FEATURE(REMOTE_WAKEUP)`，處理完就 `USBD_CtlSendStatus()` 後 return
其他請求一律丟回 ST 既有流程（`USBD_LL_SetupStage`）。
同時，在 `USBD_CUSTOM_HID_Setup()` 裡，凡是 `USB_REQ_TYPE_VENDOR` 且 `bRequest==0x20`，就直接呼叫自訂的 `USBD_CUSTOM_HID_VendorRequest()`，未檢查方向（IN/OUT）、收件者（Device/Interface/Endpoint）、也未檢查 `wIndex==0x0007` 與長度邊界，由外部 handler 來回應資料。
#### 優點
最小侵入、結構乾淨：標準請求只處理 Remote-Wakeup 的最小集，其他交還內建流程，降低與 ST Stack 的耦合度與回歸風險。  
相容度高：因沒有「吃掉」大多數請求，Windows 仍能順利透過預設路徑完成 BOS/MS OS 2.0 的查詢。  
易於除錯：Remote-Wakeup 與 Vendor-Request 的路徑清楚分離，問題定位較直覺。
#### 缺點與風險
規範檢查不嚴謹：在 Class Setup 端攔 Vendor 請求但未檢查 `bmRequestType==0xC0`（Device→Host | Vendor | Device）與 `wIndex==0x0007`，容易「看似可用」但不符合 MS OS 2.0 規範。  
可移植性與未來相容性風險：主機堆疊若更新得更嚴格，或加入其他 Vendor 路徑（例如 OS 2.0 sub-set、OS 1.0 相容路徑）時，未檢查 `wIndex` 可能導致誤回應。  
資料階段處理不完整：未在這段邏輯中明確使用 `USBD_CtlSendData()` 依 `wLength` 進行長度裁剪與分段，若外部 handler 也未處理，可能出現短傳／過長／分段錯誤。  
收件者混淆的可能：未限制 **Device recipient**（有些主機會對 Interface/Endpoint 發 Vendor 請求），若誤接管，可能干擾其它介面或類別行為。  
職責邊界模糊：在 **Class（HID）Setup** 內處理「全裝置層」的 OS 2.0 請求，對多介面或非 HID 介面（如 WinUSB/CDC）來說，權責不清，後續擴充較不利。
#### 改善建議
MS OS 2.0 的處理邏輯應統一放在 **EP0 通用層**（例如 `usbd_ctlreq.c` 或在 `HAL_PCD_SetupStageCallback()` 嚴格過濾），僅在 `bmRequestType=0xC0`（Device-to-Host｜Vendor｜Device recipient）、`bRequest=MS_OS20_VENDOR_CODE`、`wIndex=0x0007` 時回傳 `MS_OS_20_DESCRIPTOR_SET`（資料長度以 `min(wLength, sizeof(set))` 送出）；若 `bRequest=MS_OS20_VENDOR_CODE` 但參數不符合（例如 `wIndex` 非 0x0007），則回 `USBD_CtlError()`（STALL）；其它所有請求一律交回預設流程處理，以確保合規且不誤攔。  
所有控制相關的巨集建議集中管理，例如在 `usbd_conf.h` 中定義 `USBD_CLASS_BOS_ENABLED`、`USBD_LPM_ENABLED` 與 `MS_OS20_VENDOR_ENABLED`，並確保 BOS 的 Platform Capability 之 `wMSVendorCode` 與 `MS_OS20_VENDOR_CODE` 一致，方便專案層設定且確保 BOS/LPM 確實啟用。  
BOS 與 MS OS 2.0 描述符建議分檔維護：`usbd_desc.c` 可承載 BOS（包含 USB 2.0 Extension 與 MS OS 2.0 Platform Capability），`usbd_msos20.c/.h` 專責 MS OS 2.0 Descriptor Set，並務必驗證各 `wLength/wTotalLength` 與 UTF-16LE 編碼正確。  
Class 層（例如 HID Setup）不應再攔截 MS OS 2.0 的請求，該路徑交由通用層處理；Class 僅保留與該介面自身相關的其他 Vendor 請求，以避免重複或衝突。  
最後，建議加入防呆與紀錄機制：檢查 `bmRequestType/bRequest/wIndex/wLength` 合理性，在 Debug 模式下輸出事件計數與錯誤碼，以利除錯與追蹤；若裝置同時有 FS/HS 兩個 PCD 例項，兩邊均應套用相同過濾規則。
## AI Prompt
` 提示：針對STM MCU，下列的描述與本文的技術細節有一些不同，本文提供的技術細節並沒有針對wMSVendorCode 0x20、==*wIndex 0x07*==於HAL_PCD_SetupStageCallback進行辨識，而是再USBD_CUSTOM_HID_Setup前段中，直接攔截0x20進行處理。關於MS_OS_20_DESCRIPTOR_SET技術實作中只要SelectiveSuspendEnabled設為1便能達到效果。 `

```
請幫我在 STM32 專案（使用 STM32Cube HAL 與 STM32 USB Device Library）中加入「不需自訂驅動也能自動進入 USB Suspend」的功能。實作方式必須透過 Descriptor 來驅動低功耗機制，也就是在 BOS 描述符中開啟 USB 2.0 Extension 以支援 LPM，同時啟用 BOS 本身的回應，並且在 BOS 內新增一個 Microsoft OS 2.0 Platform Capability，其 GUID 為 {D8DD60DF-4589-4CC7-9CD2-659D9E648A9F}，其中 dwWindowsVersion 設定為 0x06030000，wMSVendorCode 設定為 0x20，AltEnumCode 設為 0。接著請建立一份完整的 MS_OS_20_DESCRIPTOR_SET，並且在其中至少包含兩個 Registry Property Descriptor，分別為 SelectiveSuspendEnabled 與 EnhancedPowerManagementEnabled，兩者的值都必須是 REG_DWORD little-endian 形式的 1，名稱要以 UTF-16LE 編碼正確填寫。韌體必須能處理主機透過 EP0 傳送的 Vendor Request，當 bRequest 為 0x20 且 wIndex 為 0x07 時，要能正確回傳整個 MS_OS_20_DESCRIPTOR_SET，並且根據主機的 wLength 做長度檢查，必要時允許短傳或送出 STALL。Configuration Descriptor 中的 bmAttributes bit5 需要能依據巨集切換是否支援 Remote Wakeup，預設情況為關閉，巨集設為 1 時才允許。所有新增的常數請集中定義在 usbd_conf.h，例如 USBD_CLASS_BOS_ENABLED、USBD_LPM_ENABLED 與 MS_OS20_VENDOR_ENABLED。若原始專案沒有 BOS 檔案，請新增 usbd_desc.c/.h 來定義 BOS 描述符，並新增 usbd_msos20.c/.h（或在 usbd_desc.c 中）放置 MS_OS_20_DESCRIPTOR_SET 的內容。最後請提供完整可編譯的檔案內容與 unified diff，並附上一份 README_USB_Power.md，清楚描述巨集的使用方式、Vendor Request 的流程。
```
## 結論
只要在 **Descriptor 層**把基礎功課做好(**Configuration 的 Remote Wakeup 與電力宣告**、**BOS 的 LPM 與 Platform Capability**、**MS OS 2.0 的 Registry 屬性**)，即使**不撰寫自訂驅動**，也能讓裝置在 Windows/Linux 內建驅動下，可靠地進入 **USB Suspend/選擇性省電**。BOS 負責「讓主機看見能力」，MS OS 2.0 負責「讓 Windows 形成正確的電源管理機碼」，兩者相輔相成，構成一條 **描述符驅動電源管理** 的最佳實踐路徑。
