Skip to content

Add WinUSB fallback for HIMS Braille Sense/EDGE displays blocked by Windows 11 driver signing#20555

Open
KihunJang1981 wants to merge 4 commits into
nvaccess:masterfrom
KihunJang1981:fix/hims-winusb-fallback
Open

Add WinUSB fallback for HIMS Braille Sense/EDGE displays blocked by Windows 11 driver signing#20555
KihunJang1981 wants to merge 4 commits into
nvaccess:masterfrom
KihunJang1981:fix/hims-winusb-fallback

Conversation

@KihunJang1981

Copy link
Copy Markdown

Summary of the issue:

HIMS Braille Sense/Braille EDGE (VID_045E&PID_930A/930B) connect over a custom bulk USB protocol via the vendor's himsusb.sys kernel driver (hwIo.Bulk). himsusb.sys is a legacy, non-WHCP-signed driver, and Windows 11 blocks installation of such drivers. On affected systems, hwIo.Bulk() raises and the CUSTOM protocol match is skipped entirely. Since these two PIDs have no HID or serial fallback registered, the display cannot be found via USB at all on those systems.

Description of user facing changes:

HIMS Braille Sense / Braille EDGE displays connected via USB are detected again on Windows 11 systems where himsusb.sys cannot be installed, provided the vendor's WinUSB driver package (hims_winusb.inf) is installed for the device. No change in behaviour when himsusb.sys is available, as it is tried first.

Description of developer facing changes:

  • brailleDisplayDrivers/hims.py: added a self-contained WinUSB fallback (_WinUsbBulk) used only when hwIo.Bulk() fails. It looks up the same physical device exposed via WinUSB under the device interface GUID declared in the vendor's WinUSB INF, and talks to it directly via the WinUSB API (WinUsb_Initialize/ReadPipe/WritePipe/etc.) through ctypes.
  • hwIo/base.py: fixed an unrelated handle leak in Bulk.__init__ found while debugging this fallback (see commit message for detail). Without this fix, a failed hwIo.Bulk() attempt could leave a device handle open for the life of the process, causing the WinUSB fallback's own CreateFile on the same physical device to fail with ERROR_ACCESS_DENIED.

Description of development approach:

The WinUSB access is implemented locally in hims.py (hand-declared ctypes bindings for winusb.dll, plus a small SetupDi*-based device path lookup) rather than adding a new shared hwIo/winBindings module, to keep the change scoped to this driver. _WinUsbBulk does not integrate with hwIo.bgThread's APC-based read loop, since WinUsb_ReadPipe doesn't support that completion model; it instead uses a dedicated background thread with a PIPE_TRANSFER_TIMEOUT pipe policy so reads don't block forever if the device stops responding.

Testing strategy:

Manually tested with a physical Braille Sense device on Windows 11, with himsusb.sys uninstalled/unavailable, using the WinUSB driver package from: https://hims-product.s3.ap-northeast-2.amazonaws.com/Util/HIMS_Braille_Driver_V3_1.exe

Verified:

  • Device is detected and braille output/input works via the WinUSB fallback when himsusb.sys is unavailable.
  • No behaviour change when himsusb.sys is available (CUSTOM protocol still tries hwIo.Bulk() first).

No automated unit/system tests added (device I/O against physical hardware).

Known issues with pull request:

  • Only tested on x86_64 Windows 11 with the Braille Sense; not tested on ARM64 or with Braille EDGE hardware.
  • Requires the vendor's WinUSB INF (hims_winusb.inf) to be installed separately; NVDA does not install/prompt for it.

Code Review Checklist:

  • Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

If the read pipe opens successfully but the write pipe fails, Bulk.__init__
raised immediately without closing the already-opened read handle. At that
point super().__init__() had not yet run, so the handle was never stored
(e.g. as self._file) and could not be closed later, either explicitly or via
garbage collection. The leaked handle kept the underlying device open for the
remaining lifetime of the process, which could cause other, unrelated
attempts to open the same physical device (e.g. via a different device
interface) to fail with ERROR_ACCESS_DENIED.

Close the read handle before raising when the write handle fails to open.
hwIo.Bulk talks to Braille Sense/EDGE (VID_045E&PID_930A/930B) via the
vendor's legacy himsusb.sys kernel driver, which is a custom, non-WHCP-signed
driver. Windows 11 blocks installation of such drivers, so on affected
systems hwIo.Bulk() raises and the CUSTOM protocol match is skipped. Since
these two PIDs have no HID or serial fallback registered, the display could
not be found via USB at all on those systems.

Add a self-contained WinUSB fallback: when hwIo.Bulk() fails, look for the
same physical device exposed via WinUSB (winusb.sys, an inbox driver) under
the GUID declared in the vendor's WinUSB INF (hims_winusb.inf), and talk to
it directly via the WinUSB API through ctypes. This only engages when the
himsusb.sys path is unavailable and the WinUSB INF is installed for the
device; otherwise behaviour is unchanged.

To test this path, install the WinUSB driver package from:
https://hims-product.s3.ap-northeast-2.amazonaws.com/Util/HIMS_Braille_Driver_V3_1.exe
Fill in the actual PR/issue number to replace #XXXXX once the pull request
has been opened.
@KihunJang1981
KihunJang1981 marked this pull request as ready for review July 24, 2026 06:16
@KihunJang1981
KihunJang1981 requested a review from a team as a code owner July 24, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant