libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
winusb_msos20.hpp
1#pragma once
2#include <cstddef>
3#include <cstdint>
4#include <cstring>
5
6#include "libxr_mem.hpp"
7#include "libxr_type.hpp"
8#include "usb/core/bos.hpp"
9#include "usb/core/core.hpp"
10
11namespace LibXR::USB::WinUsbMsOs20
12{
13
14// ---- constants ----
15
16// wIndex values for vendor request
17static constexpr uint16_t MSOS20_DESCRIPTOR_INDEX = 0x0007;
18static constexpr uint16_t MSOS20_SET_ALT_ENUMERATION = 0x0008;
19
20// MS OS 2.0 descriptor types
21static constexpr uint16_t MS_OS_20_SET_HEADER_DESCRIPTOR = 0x0000;
22static constexpr uint16_t MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x0001;
23static constexpr uint16_t MS_OS_20_SUBSET_HEADER_FUNCTION = 0x0002;
24static constexpr uint16_t MS_OS_20_FEATURE_COMPATIBLE_ID = 0x0003;
25static constexpr uint16_t MS_OS_20_FEATURE_REG_PROPERTY = 0x0004;
26
27// Registry property data type
28static constexpr uint16_t REG_MULTI_SZ = 0x0007;
29
30// MS OS 2.0 platform capability UUID: D8DD60DF-4589-4CC7-9CD2-659D9E648A9F
31static constexpr uint8_t MSOS20_PLATFORM_CAPABILITY_UUID[16] = {
32 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C,
33 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F,
34};
35
36// "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" + UTF-16 NUL
37static constexpr uint16_t GUID_CHARS_WITH_BRACES = 38;
38static constexpr uint16_t GUID_STR_UTF16_BYTES = (GUID_CHARS_WITH_BRACES + 1) * 2; // 78
39static constexpr uint16_t GUID_MULTI_SZ_UTF16_BYTES =
40 static_cast<uint16_t>(GUID_STR_UTF16_BYTES + 2u);
41
42// "DeviceInterfaceGUIDs" (UTF-16LE) + NUL terminator
43static constexpr uint8_t PROP_NAME_DEVICE_INTERFACE_GUIDS_UTF16[] = {
44 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00,
45 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00,
46 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00};
47static constexpr uint16_t PROP_NAME_DEVICE_INTERFACE_GUIDS_BYTES =
48 static_cast<uint16_t>(sizeof(PROP_NAME_DEVICE_INTERFACE_GUIDS_UTF16));
49
50LIBXR_PACKED_BEGIN
51
52// ---- MS OS 2.0 basic blocks ----
53
54// 9.22.1 MS OS 2.0 descriptor set header
56{
57 uint16_t wLength = 0x000A;
58 uint16_t wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR;
59 uint32_t dwWindowsVersion = 0x06030000; // 0x06030000 = Win 8.1
60 uint16_t wTotalLength = 0; // sizeof(whole set)
61};
62
63// 9.22.2 Configuration subset header
65{
66 uint16_t wLength = 0x0008;
67 uint16_t wDescriptorType = MS_OS_20_SUBSET_HEADER_CONFIGURATION;
68 // MS OS 2.0 uses a zero-based configuration index here. This is not the
69 // USB configuration descriptor's bConfigurationValue, which normally starts at 1.
70 uint8_t bConfigurationValue = 0;
71 uint8_t bReserved = 0;
72 uint16_t wTotalLength = 0; // sizeof(this cfg subset)
73};
74
75// 9.22.3 Function subset header
77{
78 uint16_t wLength = 0x0008;
79 uint16_t wDescriptorType = MS_OS_20_SUBSET_HEADER_FUNCTION;
80 uint8_t bFirstInterface = 0;
81 uint8_t bReserved = 0;
82 uint16_t wTotalLength = 0; // sizeof(this function subset)
83};
84
85// 9.22.4 Compatible ID descriptor
87{
88 uint16_t wLength = 0x0014;
89 uint16_t wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID;
90
91 // "WINUSB" + padding NULs
92 uint8_t CompatibleID[8] = {'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00};
93
94 // Optional; usually all zeros
95 uint8_t SubCompatibleID[8] = {0};
96};
97
98// 9.22.5 Registry property descriptor header (common part)
100{
101 uint16_t wLength = 0; // sizeof(whole reg property feature)
102 uint16_t wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY;
103 uint16_t wPropertyDataType = 0; // e.g. REG_MULTI_SZ
104 uint16_t wPropertyNameLength = 0;
105};
106
107// ---- MS OS 2.0 Platform Capability (BOS Device Capability) ----
108//
109// Exposed via BOS Platform Capability. Host can query the MS OS 2.0 descriptor
110// set using a vendor request with matching bMS_VendorCode and wIndex=0x0007.
112{
113 uint8_t bLength = 0x1C;
114 uint8_t bDescriptorType = 0x10; // DEVICE_CAPABILITY
115 uint8_t bDevCapabilityType = 0x05; // PLATFORM
116 uint8_t bReserved = 0x00;
117
118 uint8_t PlatformCapabilityUUID[16] = {
119 0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C,
120 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F,
121 };
122
123 uint32_t dwWindowsVersion = 0x06030000; // Win 8.1+
124 uint16_t wMSOSDescriptorSetTotalLength = 0; // sizeof(MS OS 2.0 set)
125 uint8_t bMS_VendorCode = 0x20; // vendor request bRequest
126 uint8_t bAltEnumCode = 0x00; // optional, often 0
127};
128
129template <uint16_t PropertyDataBytes>
131{
132 static_assert(PropertyDataBytes >= 4u,
133 "REG_MULTI_SZ property data must fit the double-NUL terminator");
134
136 uint8_t name[PROP_NAME_DEVICE_INTERFACE_GUIDS_BYTES];
137 uint16_t wPropertyDataLength = 0;
138 uint8_t data[PropertyDataBytes] = {};
139
140 void Init(const char* guid)
141 {
143 header.wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY;
144 header.wPropertyDataType = REG_MULTI_SZ;
145 header.wPropertyNameLength = PROP_NAME_DEVICE_INTERFACE_GUIDS_BYTES;
146 Memory::FastCopy(name, PROP_NAME_DEVICE_INTERFACE_GUIDS_UTF16,
147 PROP_NAME_DEVICE_INTERFACE_GUIDS_BYTES);
148
149 const size_t max_guid_chars =
150 (PropertyDataBytes >= 4u) ? ((PropertyDataBytes - 4u) / 2u) : 0u;
151 size_t guid_len = 0u;
152 if (guid != nullptr)
153 {
154 while (guid[guid_len] != '\0' && guid_len < GUID_CHARS_WITH_BRACES &&
155 guid_len < max_guid_chars)
156 {
157 data[guid_len * 2u] = static_cast<uint8_t>(guid[guid_len]);
158 data[guid_len * 2u + 1u] = 0x00u;
159 ++guid_len;
160 }
161 }
162
163 if (PropertyDataBytes >= 4u)
164 {
165 data[guid_len * 2u] = 0x00u;
166 data[guid_len * 2u + 1u] = 0x00u;
167 data[guid_len * 2u + 2u] = 0x00u;
168 data[guid_len * 2u + 3u] = 0x00u;
169 }
170
171 wPropertyDataLength = static_cast<uint16_t>((guid_len * 2u) + 4u);
172 header.wLength = static_cast<uint16_t>(sizeof(*this));
173 }
174};
175
176template <uint16_t PropertyDataBytes>
178{
179 MsOs20SetHeader set;
182
183 void Init(const char* guid, uint32_t windows_version = 0x06030000)
184 {
186
187 set.wLength = static_cast<uint16_t>(sizeof(MsOs20SetHeader));
188 set.wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR;
189 set.dwWindowsVersion = windows_version;
190 set.wTotalLength = static_cast<uint16_t>(sizeof(*this));
191
192 compat.wLength = static_cast<uint16_t>(sizeof(MsOs20FeatureCompatibleId));
193 compat.wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID;
194
195 prop.Init(guid);
196 }
197};
198
199template <uint16_t PropertyDataBytes>
201{
202 MsOs20SetHeader set;
207
208 void Init(uint8_t configuration_index, uint8_t first_interface, const char* guid,
209 uint32_t windows_version = 0x06030000)
210 {
212
213 set.wLength = static_cast<uint16_t>(sizeof(MsOs20SetHeader));
214 set.wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR;
215 set.dwWindowsVersion = windows_version;
216 set.wTotalLength = static_cast<uint16_t>(sizeof(*this));
217
218 cfg.wLength = static_cast<uint16_t>(sizeof(cfg));
219 cfg.wDescriptorType = MS_OS_20_SUBSET_HEADER_CONFIGURATION;
220 cfg.bConfigurationValue = configuration_index;
221 cfg.bReserved = 0u;
222 cfg.wTotalLength = static_cast<uint16_t>(
223 sizeof(*this) -
225
226 func.wLength = static_cast<uint16_t>(sizeof(func));
227 func.wDescriptorType = MS_OS_20_SUBSET_HEADER_FUNCTION;
228 func.bFirstInterface = first_interface;
229 func.bReserved = 0u;
230 func.wTotalLength = static_cast<uint16_t>(
231 sizeof(*this) -
233
234 compat.wLength = static_cast<uint16_t>(sizeof(MsOs20FeatureCompatibleId));
235 compat.wDescriptorType = MS_OS_20_FEATURE_COMPATIBLE_ID;
236
237 prop.Init(guid);
238 }
239
240 void SetFirstInterface(uint8_t first_interface)
241 {
242 func.bFirstInterface = first_interface;
243 }
244};
245
246LIBXR_PACKED_END
247
248// ---- sanity checks ----
249static_assert(sizeof(MsOs20SetHeader) == 10, "SetHeader size mismatch");
250static_assert(sizeof(MsOs20SubsetHeaderConfiguration) == 8, "CfgHeader size mismatch");
251static_assert(sizeof(MsOs20SubsetHeaderFunction) == 8, "FuncHeader size mismatch");
252static_assert(sizeof(MsOs20FeatureCompatibleId) == 20, "CompatibleId size mismatch");
253static_assert(sizeof(MsOs20PlatformCapability) == 28, "PlatformCapability size mismatch");
255 0x00A2,
256 "Device-scoped WinUSB MS OS 2.0 set size mismatch");
258 0x00B2,
259 "Function-scoped WinUSB MS OS 2.0 set size mismatch");
260
261// ---- platform capability helper ----
262
263inline void init_msos20_platform_capability(MsOs20PlatformCapability& cap,
264 uint16_t msos_descriptor_set_total_length,
265 uint8_t vendor_code = 0x20,
266 uint32_t windows_version = 0x06030000)
267{
269 Memory::FastCopy(cap.PlatformCapabilityUUID, MSOS20_PLATFORM_CAPABILITY_UUID,
270 sizeof(MSOS20_PLATFORM_CAPABILITY_UUID));
271 cap.dwWindowsVersion = windows_version;
272 cap.wMSOSDescriptorSetTotalLength = msos_descriptor_set_total_length;
273 cap.bMS_VendorCode = vendor_code;
274 cap.bAltEnumCode = 0x00;
275}
276
277// ---- BOS capability wrapper ----
278//
279// Purpose:
280// - Register a BOS Platform Capability (MS OS 2.0) to BosManager.
281// - Handle vendor requests:
282// - wIndex=0x0007 (IN): return MS OS 2.0 descriptor set.
283// - wIndex=0x0008 (OUT): Set Alt Enumeration (usually no data stage).
285{
286 public:
287 explicit MsOs20BosCapability(LibXR::ConstRawData descriptor_set,
288 uint8_t vendor_code = 0x20,
289 uint32_t windows_version = 0x06030000)
290 : descriptor_set_(descriptor_set),
291 vendor_code_(vendor_code),
292 windows_version_(windows_version)
293 {
294 RefreshPlatformCap();
295 }
296
297 void SetDescriptorSet(LibXR::ConstRawData descriptor_set)
298 {
299 descriptor_set_ = descriptor_set;
300 RefreshPlatformCap();
301 }
302
303 void SetVendorCode(uint8_t vendor_code)
304 {
305 vendor_code_ = vendor_code;
306 platform_cap_.bMS_VendorCode = vendor_code_;
307 }
308
310 {
311 return LibXR::ConstRawData(&platform_cap_, sizeof(platform_cap_));
312 }
313
314 ErrorCode OnVendorRequest(bool /*in_isr*/, const SetupPacket* setup,
315 LibXR::USB::BosVendorResult& result) override
316 {
317 if (setup == nullptr)
318 {
319 return ErrorCode::ARG_ERR;
320 }
321
322 const uint8_t BM = setup->bmRequestType;
323
324 // type must be Vendor (bits[6:5] == 10)
325 if ((BM & 0x60) != 0x40)
326 {
328 }
329
330 // recipient must be Device (bits[4:0] == 00000)
331 if ((BM & 0x1F) != 0x00)
332 {
334 }
335
336 // vendor code must match
337 if (setup->bRequest != vendor_code_)
338 {
340 }
341
342 // wIndex == 0x0007: MS OS 2.0 descriptor set (Vendor IN)
343 if (setup->wIndex == MSOS20_DESCRIPTOR_INDEX)
344 {
345 // direction must be IN
346 if ((BM & 0x80) == 0)
347 {
349 }
350
351 if (descriptor_set_.addr_ == nullptr || descriptor_set_.size_ == 0 ||
352 descriptor_set_.size_ > 0xFFFF)
353 {
354 return ErrorCode::ARG_ERR;
355 }
356
357 // Host wLength==0 is meaningless here; reject.
358 if (setup->wLength == 0)
359 {
361 }
362
363 result.handled = true;
364 result.in_data = descriptor_set_; // USB stack truncates by wLength
365 result.write_zlp = false;
366 result.early_read_zlp = true;
367 return ErrorCode::OK;
368 }
369
370 // wIndex == 0x0008: Set Alt Enumeration (Vendor OUT, usually wLength==0)
371 if (setup->wIndex == MSOS20_SET_ALT_ENUMERATION)
372 {
373 // direction should be OUT
374 if ((BM & 0x80) != 0)
375 {
376 // Strict behavior: reject IN.
378 }
379
380 // Usually no data stage. If wLength != 0, we still ACK to keep flow smooth.
381 if (setup->wLength != 0)
382 {
383 // no-op
384 }
385
386 result.handled = true;
387 result.in_data = {nullptr, 0};
388 result.write_zlp = true; // complete status stage
389 result.early_read_zlp = true;
390 return ErrorCode::OK;
391 }
392
394 }
395
396 private:
397 void RefreshPlatformCap()
398 {
399 const uint16_t total_length = (descriptor_set_.size_ <= 0xFFFF)
400 ? static_cast<uint16_t>(descriptor_set_.size_)
401 : 0u;
402 init_msos20_platform_capability(platform_cap_, total_length, vendor_code_,
403 windows_version_);
404 }
405
406 // Cached MS OS 2.0 descriptor set bytes.
407 LibXR::ConstRawData descriptor_set_{nullptr, 0};
408
409 // Vendor request bRequest code used for MS OS 2.0 query.
410 uint8_t vendor_code_ = 0x20;
411
412 // Windows version field in Platform Capability.
413 uint32_t windows_version_ = 0x06030000;
414
415 // BOS Platform Capability descriptor block.
416 MsOs20PlatformCapability platform_cap_{};
417};
418
419} // namespace LibXR::USB::WinUsbMsOs20
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
static void FastCopy(void *dst, const void *src, size_t size)
快速内存拷贝 / Fast memory copy
Definition libxr_mem.cpp:5
BOS 能力接口 / BOS capability interface.
Definition bos.hpp:57
ErrorCode OnVendorRequest(bool, const SetupPacket *setup, LibXR::USB::BosVendorResult &result) override
处理该能力相关 Vendor 请求 / Handle vendor request for this capability
LibXR::ConstRawData GetCapabilityDescriptor() const override
返回能力块(不含 BOS 头) Return capability block (without BOS header).
ErrorCode
定义错误码枚举
@ NOT_SUPPORT
不支持 | Not supported
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
Vendor 请求处理结果(EP0 控制传输) Vendor request handling result for EP0 control transfers.
Definition bos.hpp:45
bool handled
已处理该请求 / Request consumed by capability
Definition bos.hpp:46
ConstRawData in_data
IN 数据阶段返回数据 / IN data stage payload.
Definition bos.hpp:47
bool write_zlp
状态阶段发送 ZLP / Send ZLP at status stage
Definition bos.hpp:48
bool early_read_zlp
提前准备 OUT 以适配主机短读 / Arm OUT early for short-read tolerance
Definition bos.hpp:49
USB 标准请求 SETUP 包(固定8字节) Standard USB setup packet (8 bytes)
Definition core.hpp:58
uint16_t wIndex
对象索引,如接口号或端点号 / Index (e.g., interface or endpoint)
Definition core.hpp:64
uint16_t wLength
数据阶段长度 / Number of bytes in data stage
Definition core.hpp:65
uint8_t bRequest
请求码 / Request code (e.g., GET_DESCRIPTOR)
Definition core.hpp:61