{"id":48276669,"url":"https://github.com/nomi-san/parsec-vusb","last_synced_at":"2026-04-09T02:00:30.059Z","repository":{"id":348652165,"uuid":"1199178667","full_name":"nomi-san/parsec-vusb","owner":"nomi-san","description":"Reverse-engineered docs and tools for Parsec Virtual USB Driver (PVUD)","archived":false,"fork":false,"pushed_at":"2026-04-02T05:48:20.000Z","size":3027,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-08T01:02:50.787Z","etag":null,"topics":["ds5","gamepad","microphone","parsec","reverse-engineering","virtual-drivers","virtual-usb","wacom"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nomi-san.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-02T05:40:25.000Z","updated_at":"2026-04-07T01:22:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nomi-san/parsec-vusb","commit_stats":null,"previous_names":["nomi-san/parsec-vusb"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nomi-san/parsec-vusb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nomi-san%2Fparsec-vusb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nomi-san%2Fparsec-vusb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nomi-san%2Fparsec-vusb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nomi-san%2Fparsec-vusb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nomi-san","download_url":"https://codeload.github.com/nomi-san/parsec-vusb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nomi-san%2Fparsec-vusb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31581864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ds5","gamepad","microphone","parsec","reverse-engineering","virtual-drivers","virtual-usb","wacom"],"created_at":"2026-04-04T22:36:15.946Z","updated_at":"2026-04-09T02:00:29.983Z","avatar_url":"https://github.com/nomi-san.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/pvud-icon.png\" width=\"96\" /\u003e\n\u003c/p\u003e\n\u003ch1 align=\"center\"\u003eParsec VUSB\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  Reverse-engineered docs \u0026amp; tools for Parsec Virtual USB Driver (PVUD)\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PARSEC-150--101-blue?style=for-the-badge\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PVUD-0.3.10-green?style=for-the-badge\" /\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n## ℹ About\n\nParsec VUSB (officially **PVUD** — Parsec Virtual USB Driver) is a kernel-mode driver that creates a virtual USB host controller on Windows. Through this virtual bus, the Parsec host can plug in emulated USB devices — gamepads, microphones, mice, tablets, and passthrough USB devices — making them appear as real hardware to the guest OS and applications.\n\nThis project documents the driver's IOCTL interface, device lifecycle, and USB descriptor formats, all reverse-engineered from `parsecd-150-101.dll` (Windows x64, Parsec SDK 6.0). The goal is to enable standalone use of the PVUD driver independent of the Parsec app, similar to what my [parsec-vdd](https://github.com/nomi-san/parsec-vdd) does for the Parsec Virtual Display Driver.\n\n### Supported virtual devices\n\n| Port | Type | Description |\n|:----:|------|-------------|\n| 1 | Xbox 360 Controller | Standard XInput gamepad |\n| 2 | DualSense (PS5) | Sony PS5 controller with isochronous endpoints |\n| 3 | Virtual Microphone | USB audio device (isochronous transfer) |\n| 4 | Virtual Mouse | Dummy HID mouse — keeps cursor visible, does not generate input |\n| 5 | Virtual Tablet | Wacom Intuos Pro (M) digitizer for pen/tablet passthrough |\n| 7 | DualShock 4 (PS4) | Sony PS4 controller |\n| 8 | Virtual Yubikey | USB passthrough for Yubikey security keys (via LibUSB) |\n| — | Virtual Camera | **Not PVUD** — uses Windows 11 Media Foundation (`MFCreateVirtualCamera`) |\n\n### Requirements\n\n- Windows 10 or later\n- Parsec VUSB driver installed (`Root\\Parsec\\VUSBA`)\n- Driver version ≥ **5** (older versions lack microphone and advanced endpoint support)\n\n\u003e **ViGEm fallback:** When PVUD is unavailable or version \u003c 5, Parsec falls back to [ViGEm Bus](https://github.com/nefarius/ViGEmBus) for gamepad emulation (Xbox 360 and DualShock 4 only).\n\n---\n\n## 🏗 Architecture\n\nPVUD registers a device interface identified by the GUID `{25EFC209-91FE-4460-A4B7-6A9E31C0D0F1}`. The host process opens this interface via `SetupDiGetClassDevs` + `CreateFileW`, then communicates entirely through `DeviceIoControl()`.\n\n```mermaid\nflowchart LR\n\n  subgraph UM[\"User Mode (parsecd/app)\"]\n    XBOX[\"Xbox 360\"]\n    DS[\"DualSense\"]\n    MIC[\"Mic\"]\n    ETC[\"...\"]\n  end\n\n  IOCTL[\"PVUD IOCTL Interface\\nDeviceIoControl(hAdapter, ...)\"]\n\n  subgraph KM[\"Kernel Mode\"]\n    VUSB[\"Parsec Virtual USB Host Controller\\n(Root\\\\Parsec\\\\VUSBA)\"]\n  end\n\n  XBOX --\u003e IOCTL\n  DS --\u003e IOCTL\n  MIC --\u003e IOCTL\n  ETC --\u003e IOCTL\n\n  IOCTL --\u003e VUSB\n```\n\n---\n\n## 🔄 Device Lifecycle\n\nEvery virtual USB device follows the same lifecycle:\n\n```\n1. Open Adapter\n   PCVUD_open_adapter (IOCTL 0x2A6404 — version handshake)\n       ↓\n2. Create Device\n   PCVUD_device_create_ioctl (IOCTL 0x2AE804 — send full USB descriptor)\n       ↓\n3. Configure Endpoints\n   PCVUD_set_endpoints      (IOCTL 0x2AA808 — set endpoint addresses)\n   PCVUD_set_endpoint_types  (IOCTL 0x2AA81C — optional, for isochronous)\n       ↓\n4. Plug Into Bus\n   PCVUD_device_plug (IOCTL 0x2AAC04 — device appears to Windows)\n       ↓\n5. I/O Loop\n   PCVUD_event_poll_loop  (IOCTL 0x2AA810 — blocking event loop)\n   PCVUD_endpoint_write   (IOCTL 0x2AB008 — host → device)\n   PCVUD_endpoint_read    (IOCTL 0x2AF004 — device → host)\n       ↓\n6. Unplug\n   PCVUD_device_unplug (IOCTL 0x2AAC08 — cleanup + remove)\n```\n\n---\n\n## 📋 IOCTL Reference\n\n### Adapter Management\n\n| IOCTL | Name | Dir | Buffer | Description |\n|-------|------|-----|--------|-------------|\n| `0x2A6404` | `open_adapter` | In/Out | 20 B (v2) or 12 B (v1) | Version handshake. Tries 20 B first, falls back to 12 B on `ERROR_INVALID_USER_BUFFER` (1784). |\n| `0x2AE408` | `query_version` | In/Out | 16 B | Query driver version. |\n| `0x2AE804` | `device_create` | In/Out | ~556+ B (variable) | Create device with full USB descriptor. Returns **600-byte** (`0x258`) device context. |\n| `0x2AE80C` | `get_persisted_slots` | In/Out | 96 B | Get port mapping. Returns bitmask of occupied ports (up to 16 slots). |\n| `0x2AE810` | `open_device_by_port` | In/Out | 9 B → variable | Get endpoint list for an existing device. First call returns count, second returns list. |\n\n### Device Control\n\n| IOCTL | Name | Dir | In Size | Description |\n|-------|------|-----|---------|-------------|\n| `0x2AAC04` | `device_plug` | In | 16 B | Plug device into virtual bus. |\n| `0x2AAC08` | `device_unplug` | In | 8 B | Unplug device. Closes endpoint events and handles. |\n| `0x2AA808` | `set_endpoints` | In | 9 + N B | Set endpoint addresses. Header: size(4) + port_id(4) + count(1), then N endpoint bytes. |\n| `0x2AA81C` | `set_endpoint_types` | In | 9 + N B | Set transfer types (bulk, interrupt, isochronous). Same header format. |\n| `0x2AA810` | `event_poll` | In/Out | 16 B | Blocking event poll. Loops on `GetOverlappedResult(TRUE)` until error 5 (`ACCESS_DENIED`) or 995 (`OPERATION_ABORTED`). |\n\n### Endpoint Data Transfer\n\n| IOCTL | Name | Dir | Layout | Description |\n|-------|------|-----|--------|-------------|\n| `0x2AB008` | `endpoint_write` | Out→Driver | 13 + data B | Write data to endpoint. |\n| `0x2AF004` | `endpoint_read` | Driver→Host | 13 + data B | Read data from endpoint. Error 259 = pending (`ERROR_NO_MORE_ITEMS`). |\n| `0x2AB010` | `write_status` | Out→Driver | 25 + data B | Write with transfer status. |\n| `0x2AB024` | `write_alt` | Out→Driver | 25 + data B | Write data (alternate path). |\n| `0x2AF00C` | `read_meta` | Driver→Host | 25 + data B | Read with metadata. |\n| `0x2AF01C` | `read_timeout` | Driver→Host | 33 + data B | Read with timeout (9800 μs). |\n| `0x2AF020` | `read_status` | Driver→Host | 17 + data B | Read with transfer status. |\n| `0x2AA80C` | `control_xfer` | Out→Driver | 20–71 B | Control/descriptor transfer. |\n\n### Isochronous Transfer (Audio)\n\n| IOCTL | Name | Dir | Buffer | Description |\n|-------|------|-----|--------|-------------|\n| `0x2AF014` | `iso_setup` | In/Out | 149 B | Setup isochronous transfer. Returns number of packets (typically 10). |\n| `0x2AB018` | `iso_write` | Out→Driver | 12×packets + 1001 B | Write audio data. 960 samples split across packets. Timeout: 10 ms. |\n\n---\n\n## 📦 Buffer Formats\n\n### Endpoint I/O Header\n\nAll endpoint read/write IOCTLs (`0x2AB008`, `0x2AF004`) share this header:\n\n```c\nstruct pvud_endpoint_io {\n    uint32_t total_size;     // [0..3]   sizeof(header) + data_length\n    uint32_t port_id;        // [4..7]   device port identifier\n    uint8_t  endpoint_addr;  // [8]      USB endpoint address (e.g. 0x81 = IN EP1)\n    uint32_t data_length;    // [9..12]  length of payload\n    uint8_t  data[];         // [13..]   payload bytes\n};\n```\n\n### Device Plug Buffer\n\n```c\nstruct pvud_device_plug {\n    uint32_t size;        // [0..3]   total struct size (16)\n    uint32_t port_id;     // [4..7]   assigned port identifier\n    uint8_t  port_a;      // [8]      sub-port A\n    uint8_t  port_b;      // [9]      sub-port B\n    uint16_t padding;     // [10..11]\n    uint32_t port_num;    // [12..15] device port number (1–8)\n};\n```\n\n### Device Context (Returned by `device_create`)\n\nThe `device_create` IOCTL (`0x2AE804`) returns a **600-byte** (`0x258`) structure:\n\n```c\nstruct pvud_device_ctx {\n    void*    device_handle;        // [0]     driver handle\n    uint32_t reserved;             // [8]\n    uint32_t port_id;              // [8]     assigned port ID\n    uint32_t device_type;          // [12]    1=Xbox360, 2=DS5, 3=Mic, 4=Mouse, 5=Tablet, 7=DS4, 8=Yubikey\n    void*    control_event;        // [16]    event for control requests\n    void*    cancelled_event;      // [24]    event for cancellation\n    void*    out_ep_events;        // [32]    array of OUT endpoint wait events\n    void*    in_ep_events;         // [40]    array of IN endpoint wait events\n    uint8_t  out_ep_addrs[256];    // [48]    OUT endpoint address list\n    uint8_t  out_ep_count;         // [303]   number of OUT endpoints\n    uint8_t  in_ep_addrs[256];     // [304]   IN endpoint address list\n    uint8_t  in_ep_count;          // [559]   number of IN endpoints\n    uint8_t  is_persistent;        // [560]   persistence flag\n    //       ...\n    void*    handler;              // [568]   event handler callback\n    void*    owner_ctx;            // [576]   back-pointer to owner\n    //       ...\n    uint8_t  unplugged;            // [592]   set to 1 after unplug\n};\n```\n\n---\n\n## 🎮 Virtual Devices\n\n### Xbox 360 Controller (Port 1)\n\nThe most common gamepad emulation. Creates a standard XInput-compatible device.\n\n| Property | Value |\n|----------|-------|\n| bcdUSB | `0x0200` |\n| bDeviceClass | `0xFF` (vendor-specific) |\n| Port | 1 |\n| Endpoints | 7-byte config: `[0x84B00001, 0x0201, 0x03]` |\n| Handler | `sub_1801B9220` |\n| Context size | 0x30 bytes (smallest) |\n\nUSB descriptor is built inline with a ~153-byte HID report descriptor. VID:`045E` PID:`028E`. Manufacturer: `L\"©Microsoft Corporation\"`, Product: `L\"Controller\"`.\n\n### DualShock 4 (Port 7)\n\nSony PS4 controller emulation.\n\n| Property | Value |\n|----------|-------|\n| USB descriptor | Inline (VID:`054C` PID:`05C4`, \"Sony Corp.\" / \"Wireless Controller\") |\n| Port | 7 |\n| Endpoints | 2 endpoints, config value `0x04080484` |\n| Handler | `sub_1801B76B0` |\n| Context size | 0x38 bytes |\n\n### DualSense (Port 2)\n\nSony PS5 controller. More complex than DS4 — uses isochronous endpoint types.\n\n| Property | Value |\n|----------|-------|\n| USB descriptor | Inline (VID:`054C` PID:`0CE6`, \"Sony Interactive Entertainment\" / \"Wireless Controller\") |\n| Port | 2 |\n| Endpoints | 4 endpoints, config `0x03850081` |\n| Endpoint types | `0x01010202` (4 bytes — includes isochronous) |\n| Handler | `sub_1801B81F0` |\n| Context size | 0x38 bytes |\n\nDualSense additionally registers a USB class GUID via `sub_1801B97C0` and creates a separate I/O thread (`sub_1801B8760`). It is the only gamepad that uses `set_endpoint_types` (`0x2AA81C`).\n\n### Virtual Microphone (Port 3)\n\nUSB audio device using isochronous transfers for real-time audio streaming.\n\n| Property | Value |\n|----------|-------|\n| bcdUSB | `0x0110` |\n| Device class | Vendor-specific audio (`0xF0510800`) |\n| Port | 3 |\n| Endpoint | `0x81` (IN EP1, isochronous) |\n| Audio format | 960 samples/frame, 10 packets × 96 samples |\n\n**Lifecycle:**\n```\nhost_vmicrophone_manage (state machine: 0=destroy, 1=start, 2=persist)\n    ↓\nhost_vmicrophone_init\n    ├── audio_decode_init\n    ├── PCVUD_microphone_open_by_port (if persistent slot exists)\n    │   └── IOCTL 0x2AE810\n    ├── PCVUD_microphone_device_create (if new)\n    │   └── IOCTL 0x2AE804\n    ├── Audio decode thread\n    └── Isochronous transfer thread\n        └── IOCTL 0x2AF014 (setup) + 0x2AB018 (write)\n```\n\n**Isochronous transfer detail:**\n1. **Setup** (IOCTL `0x2AF014`): 149-byte buffer, endpoint `0x81`, requests 10 packets\n2. **Write** (IOCTL `0x2AB018`): `12 × packets + 1001` bytes\n   - 960 samples split into 10 packets of 96 samples each\n   - Packet descriptor: `[offset:4][length:4][status:4]` (12 bytes per packet)\n   - Audio data follows the descriptors\n   - Timeout: 10 ms\n\n**Version requirement:** VUSB driver type 2 with version ≥ 5, or type \u003e 2.\n\n### Virtual Mouse (Port 4)\n\nDummy HID mouse device created during owner connections. Its sole purpose is to keep the mouse cursor visible on the host even if no physical mouse is plugged in. **This mouse does not generate any input** — it's purely a presence device.\n\n| Property | Value |\n|----------|-------|\n| bcdUSB | `0x0200` |\n| Interface | `0xBEFC0000` (vendor-specific) |\n| Port | 4 |\n| Endpoints | 3: `[0x81, 0x83, 0x85]` (IN, interrupt/isochronous) |\n| HID report | 84 bytes (21 DWORDs) |\n| Handler | `sub_1801BBC30` |\n\nCreated when \"Virtual Mouse\" is enabled in host settings (on by default).\n\n### Virtual Tablet (Port 5)\n\nEmulates a **Wacom Intuos Pro (M)** digitizer for native pen/tablet passthrough. When connecting from a Windows client with a 2017+ Wacom Tablet, this replaces Windows Ink pen injection and simulates a native Wacom tablet experience with support for tilt, rotation, and pressure.\n\n| Property | Value |\n|----------|-------|\n| bcdUSB | `0x0200` |\n| Interface | `0x056A0800` (Wacom digitizer class) |\n| Port | 5 |\n| Endpoints | 3: `[0x81, 0x83, 0x85]` (IN, isochronous) |\n| Report descriptor | 75-byte HID descriptor (Wacom pen digitizer) |\n| Manufacturer/Product | `\"W\"` / `\"W\"` (Wacom) |\n| Handler | `sub_1801BC060` |\n| Key functions | `VirtualWacomIntuosProMDeviceFormatPenReport`, `VirtualWacomIntuosProMDeviceSubmitPenReport` |\n\nInitialization allocates a 0x70-byte context and creates **4 threads**:\n1. Input processing (`sub_1800B8860`)\n2. Output processing (`sub_1800B8F90`)\n3. State management (`sub_1800B90A0`)\n4. VUSB I/O (`sub_1801BCB30`)\n\nUses two queues: 15 items × 0x7A4 bytes each (input and output) with mutex synchronization.\n\n\u003e **Warp feature:** Virtual Tablet is a Parsec Warp feature (requires subscription).\n\n### Virtual Yubikey Passthrough (Port 8)\n\nUSB device passthrough for Yubikey security keys (generation 5). Unlike other virtual devices which have hardcoded USB descriptors, the Yubikey passthrough captures a **real physical Yubikey** on the client via [LibUSB](https://libusb.info/) and forwards the full USB descriptor + endpoint data over the network to a virtual USB device on the host.\n\n| Property | Value |\n|----------|-------|\n| Port | 8 |\n| Device type | 8 (passthrough) |\n| USB descriptor | Captured from real device (variable) |\n| Context size | 0x70 bytes |\n| Create function | `PCVUD_passthroughusb_device_create` (via `PCVUD_device_create_wrapper`) |\n| Control handler | `PCVUD_passthroughusb_handle_control_request` (`sub_1801BAE80`) |\n\n**Client-side:** Uses LibUSB (`libusb-1.0.dll`) to access the physical Yubikey:\n- Parsec can auto-install LibUSB, or the user can self-manage (`C:\\Windows\\System32\\libusb-1.0.dll`)\n- LibUSB functions loaded: `libusb_init`, `libusb_open`, `libusb_control_transfer`, `libusb_interrupt_transfer`, `libusb_bulk_transfer`, `libusb_claim_interface`, etc.\n\n**Host-side lifecycle:**\n```\nClient captures Yubikey USB descriptor via LibUSB\n    ↓\nMessage type 15: Host receives USB descriptor (product, manufacturer, extended desc)\n    ↓\nMessage type 11: Host creates virtual USB device\n    ├── PCVUD_device_create_wrapper (IOCTL 0x2AE804)\n    ├── PCVUD_device_plug (port 8)\n    ├── Control request handler thread\n    ├── Event poll thread\n    └── I/O transfer thread\n    ↓\nMessage type 10: Endpoint data forwarded to/from device\n    ↓\nMessage type 12/13: Device cleanup / unplug\n```\n\n### Virtual Camera (Not PVUD)\n\nThe Virtual Camera is **not** a PVUD device. It uses the Windows 11 **Media Foundation** API (`MFCreateVirtualCamera` from `mfsensorgroup.dll`) to create a virtual camera source. This requires Windows 11 — on Windows 10, the feature is unavailable.\n\nThe camera captures video on the client side and streams it to the host where it appears as a standard camera device via Media Foundation's virtual camera framework.\n\n---\n\n## 🔌 Host Integration\n\n### Session Startup Order\n\nWhen a Parsec host session begins (`host_session_start` — `0x1800A10B0`):\n\n1. **VUSB adapter init** — version check, ViGEm fallback\n2. VDD custom resolutions (3 slots)\n3. VDD virtual displays + privacy thread\n4. **Virtual mouse** (if privacy mode enabled)\n5. Screen blanking via `PostMessageW(WM_SYSCOMMAND, SC_MONITORPOWER)`\n6. Resolution clamping (max 8192×4320)\n7. Host video capture thread\n\n### VUSB Adapter Init\n\n```c\n// host_vusb_adapter_init (0x1800A7500)\nif (!PCVUD_is_operable || version \u003c 5) {\n    // Fall back to ViGEm bus\n    LOG(\"Using %s for host gamepad input\", \"ViGEm\");\n} else {\n    // Allocate 0x58 context, create queue(5 items, 4 bytes each)\n    // Start worker thread\n    LOG(\"Using %s for host gamepad input\", \"PCVUD\");\n}\n```\n\n### Controller Plug Dispatch\n\nWhen a client connects a gamepad (`host_controller_plug` — `0x180051070`):\n\n| Type | Controller | VUSB Function | ViGEm Fallback | Port |\n|:----:|-----------|---------------|----------------|:----:|\n| 1 | Xbox 360 | `PCVUD_xbox360_device_create` | `vigem_target_x360_alloc` | 1 |\n| 2 | DualShock 4 | `PCVUD_ds4_device_create` | `vigem_target_ds4_alloc` | 7 |\n| 3 | DualSense | `PCVUD_ds5_device_create` | `vigem_target_ds4_alloc` | 2 |\n\n\u003e Note: DualSense falls back to DS4 on ViGEm since ViGEm has no native DS5 target.\n\n### Adapter Recovery\n\nA monitoring loop (`host_vdd_adapter_monitor` — `0x1800A71E0`) handles driver recovery:\n- Polls every 100 ms (idle) or 1000 ms (active)\n- Attempts `adapter_create` up to 3 times\n- 5-fault-in-5-second limit before giving up\n- Dynamic adapter re-creation on driver recovery\n\n---\n\n## 📍 Function Map\n\nAddresses from `parsecd-150-101.dll`:\n\n\u003cdetails\u003e\n\u003csummary\u003eCore VUSB functions\u003c/summary\u003e\n\n| Address | Name | Description |\n|---------|------|-------------|\n| `0x1801B6AE0` | `PCVUD_open_adapter` | Open adapter, version handshake |\n| `0x1801AFBF0` | `PCVUD_check_driver_version` | Registry + GetFileVersionInfo |\n| `0x1801B4A50` | `PCVUD_device_create_ioctl` | Core device creation (IOCTL 0x2AE804) |\n| `0x1801BAAD0` | `PCVUD_device_create_wrapper` | Wrapper: maps types, calls create_ioctl |\n| `0x1801B5770` | `PCVUD_device_plug` | IOCTL 0x2AAC04 — plug into bus |\n| `0x1801B58D0` | `PCVUD_device_unplug` | IOCTL 0x2AAC08 — unplug + cleanup |\n| `0x1801B64A0` | `PCVUD_set_endpoints` | IOCTL 0x2AA808 — configure endpoints |\n| `0x1801B6300` | `PCVUD_set_endpoint_types` | IOCTL 0x2AA81C — set transfer types |\n| `0x1801B5E20` | `PCVUD_endpoint_write` | IOCTL 0x2AB008 — write to endpoint |\n| `0x1801B5C80` | `PCVUD_endpoint_read` | IOCTL 0x2AF004 — read from endpoint |\n| `0x18015EF00` | `PCVUD_event_poll_loop` | IOCTL 0x2AA810 — blocking event loop |\n| `0x1801BB620` | `PCVUD_isochronous_transfer` | IOCTLs 0x2AF014 + 0x2AB018 |\n| `0x1801B5520` | `PCVUD_get_persisted_slots` | IOCTL 0x2AE80C — port mapping |\n| `0x1801B5090` | `PCVUD_open_device_by_port` | IOCTL 0x2AE810 — open existing |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDevice-specific creators\u003c/summary\u003e\n\n| Address | Name | Description |\n|---------|------|-------------|\n| `0x1801B89D0` | `PCVUD_xbox360_device_create` | Xbox 360 controller |\n| `0x1801B7050` | `PCVUD_ds4_device_create` | DualShock 4 |\n| `0x1801B7B30` | `PCVUD_ds5_device_create` | DualSense |\n| `0x1801BB1A0` | `PCVUD_microphone_device_create` | Virtual microphone |\n| `0x1801BB3D0` | `PCVUD_microphone_open_by_port` | Reopen persistent mic |\n| `0x1801BBA40` | `PCVUD_vmouse_device_create` | Virtual mouse |\n| `0x1801BCDC0` | `PCVUD_vtablet_device_create` | Virtual tablet |\n| `0x1801B71D0` | `PCVUD_vmouse_device_destroy` | Destroy virtual mouse |\n| `0x1800B9B90` | `PCVUD_passthroughusb_handler` | Yubikey passthrough message handler |\n| `0x1801BAE80` | `PCVUD_passthroughusb_control` | Yubikey control request handler |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHost integration\u003c/summary\u003e\n\n| Address | Name | Description |\n|---------|------|-------------|\n| `0x180051070` | `host_controller_plug` | Gamepad plug dispatcher |\n| `0x1800A7500` | `host_vusb_adapter_init` | VUSB adapter init + ViGEm fallback |\n| `0x1800A10B0` | `host_session_start` | Session startup orchestrator |\n| `0x1800B83D0` | `host_vmicrophone_init` | Virtual microphone init |\n| `0x1800B8260` | `host_vmicrophone_destroy` | Virtual microphone cleanup |\n| `0x1800A1DD0` | `host_vmicrophone_manage` | Microphone state machine |\n| `0x1800B9490` | `host_vtablet_init` | Virtual tablet init |\n| `0x1800B9EC0` | `host_vyubikey_loop` | Yubikey passthrough control loop |\n| `0x18003FDF0` | `mf_virtual_camera_init` | Virtual camera Media Foundation init (not PVUD) |\n\n\u003c/details\u003e\n\n---\n\n## ⚡ Quick Start (Pseudocode)\n\n```c\n// 1. Open the VUSB adapter\nHANDLE adapter = open_device(PVUD_DEVICE_GUID);\npvud_open_adapter(adapter);  // IOCTL 0x2A6404\n\n// 2. Create a virtual Xbox 360 controller\nuint8_t usb_desc[556] = { /* Xbox 360 USB descriptor */ };\npvud_device_ctx ctx;\nDeviceIoControl(adapter, 0x2AE804, usb_desc, sizeof(usb_desc),\n                \u0026ctx, sizeof(ctx), ...);\n\n// 3. Configure endpoints\nuint8_t ep_header[9] = { /* size, port_id, count */ };\nuint8_t ep_addrs[] = { 0x81, 0x02 };  // IN EP1, OUT EP2\nDeviceIoControl(adapter, 0x2AA808, ep_data, sizeof(ep_data), ...);\n\n// 4. Plug the device\npvud_device_plug plug = {\n    .size = 16, .port_id = ctx.port_id,\n    .port_a = 0, .port_b = 0, .port_num = 1\n};\nDeviceIoControl(adapter, 0x2AAC04, \u0026plug, 16, ...);\n\n// 5. Write gamepad state (in a loop)\npvud_endpoint_io pkt = {\n    .total_size = 13 + report_size,\n    .port_id = ctx.port_id,\n    .endpoint_addr = 0x02,\n    .data_length = report_size,\n};\nmemcpy(pkt.data, gamepad_report, report_size);\nDeviceIoControl(adapter, 0x2AB008, \u0026pkt, 13 + report_size, ...);\n\n// 6. Unplug when done\nDeviceIoControl(adapter, 0x2AAC08, \u0026port_id, 8, ...);\nCloseHandle(adapter);\n```\n\n---\n\n## 😥 Known Limitations\n\n- **No Linux/macOS support** — PVUD is a Windows kernel driver, Windows 10+ only\n- **Driver not publicly distributed** — must be extracted from a Parsec installation\n- **Microphone requires driver v5+** — older versions silently fail\n- **DualSense on ViGEm** — falls back to DS4 emulation (no haptics, adaptive triggers)\n- **Maximum port range** — port numbers 1–8 are hardcoded per device type\n- **USB descriptors are embedded** — changing VID/PID requires patching the binary (except Yubikey which captures real descriptors)\n- **Yubikey requires LibUSB** — must be installed on the client machine\n- **Virtual Camera requires Windows 11** — not available on Windows 10 (not a PVUD device)\n\n---\n\n## 🍻 Credits\n\n- Reverse-engineered from `parsecd-150-101.dll` using IDA Pro\n- Thanks to Parsec for building the driver\n- Inspired by my [parsec-vdd](https://github.com/nomi-san/parsec-vdd)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnomi-san%2Fparsec-vusb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnomi-san%2Fparsec-vusb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnomi-san%2Fparsec-vusb/lists"}