From 2035c0b4fe30c6180278a5c957c0c26f6ac1bac2 Mon Sep 17 00:00:00 2001 From: joyharam <150105283@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:52:01 +0900 Subject: [PATCH] Clarify Windows Access denied on USB interface 5 Point users to Zadig WinUSB binding for MI_05 and stopping official Wormhole drivers that hold exclusive claim on interface 5. Co-authored-by: Cursor --- docs/WINDOWS-ACCESS-DENIED.md | 51 +++++++++++++++++++++++++++++++++++ docs/WINDOWS.md | 11 ++++++++ juc500_xfer/usb_link.py | 43 ++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 docs/WINDOWS-ACCESS-DENIED.md diff --git a/docs/WINDOWS-ACCESS-DENIED.md b/docs/WINDOWS-ACCESS-DENIED.md new file mode 100644 index 0000000..128034a --- /dev/null +++ b/docs/WINDOWS-ACCESS-DENIED.md @@ -0,0 +1,51 @@ +# Windows 11 — Access denied (Interface 5) 해결 + +연결 시 아래 메시지가 나오면: + +``` +USB interface 5 점유 실패: [Errno 13] Access denied (insufficient permissions) +``` + +케이블은 보이지만, **Interface 5를 쓸 권한이 없다**는 뜻입니다. +(공식 Wormhole 드라이버가 Interface 5를 점유 중일 때가 가장 흔합니다.) + +## 해결 순서 + +### 1. 공식 앱 종료/제거 + +- `Wormhole`, `SmartDataLink`, `j5create` 관련 프로세스 종료 +- 설정 → 앱에서 j5create Wormhole 제거 (가능하면) + +### 2. Zadig로 Interface 5만 WinUSB 변경 + +1. [Zadig](https://zadig.akeo.ie/) 를 **관리자 권한**으로 실행 +2. **Options → List All Devices** +3. 드롭다운에서 다음 중 하나를 고릅니다. + - `Smart Data Link (Interface 5)` + - 또는 `USB ID` 가 `0711:7500` 이고 이름/인스턴스에 **MI_05** 포함 +4. 오른쪽 Driver 를 **WinUSB** 로 선택 +5. **Replace Driver** +6. USB 케이블을 뽑았다가 Windows 쪽에 다시 연결 + +> Interface 2(대용량 저장소 CD), 3·4(HID) 는 바꾸지 마세요. + +### 3. 장치 관리자 확인 + +장치 관리자 → 보기 → 디바이스(연결 기준) + +- `0711` / `Smart Data Link` 하위 **MI_05** 항목의 드라이버가 **WinUSB** 인지 확인 +- `j5create` / `Wormhole` / `OTi` 드라이버면 → 드라이버 제거 후 Zadig 다시 + +### 4. 재실행 + +```powershell +python -m juc500_xfer doctor +python -m juc500_xfer gui +``` + +그다음 **연결**. + +## 참고 + +- Access denied 는 “관리자 미실행”보다 **잘못된 USB 드라이버** 문제인 경우가 훨씬 많습니다. +- libusb DLL(`vendor\libusb-1.0.dll` → `.venv\Scripts\`)도 필요합니다. diff --git a/docs/WINDOWS.md b/docs/WINDOWS.md index 4175d53..1f96473 100644 --- a/docs/WINDOWS.md +++ b/docs/WINDOWS.md @@ -1,5 +1,16 @@ # Windows 11 설정 +## 연결 시 Access denied (Interface 5) + +``` +USB interface 5 점유 실패: [Errno 13] Access denied +``` + +→ 케이블은 보이지만 Interface 5가 WinUSB가 아닙니다. +자세한 단계: [`WINDOWS-ACCESS-DENIED.md`](WINDOWS-ACCESS-DENIED.md) + +요약: Zadig → **List All Devices** → **Smart Data Link (Interface 5) / MI_05** → **WinUSB** → Replace Driver → 케이블 재연결. + ## 소스에서 실행 (오프라인 wheel 포함) 저장소의 `vendor/wheels/pyusb-1.2.1-py3-none-any.whl` 을 사용합니다. PyPI 접속이 없어도 됩니다. diff --git a/juc500_xfer/usb_link.py b/juc500_xfer/usb_link.py index 1bc35ea..cce54f0 100644 --- a/juc500_xfer/usb_link.py +++ b/juc500_xfer/usb_link.py @@ -182,9 +182,39 @@ def _usb_err_text(exc: BaseException) -> str: return " ".join(parts).lower() -def _is_not_supported(exc: BaseException) -> bool: +def _is_access_denied(exc: BaseException) -> bool: text = _usb_err_text(exc) - return "not supported" in text or "unimplemented" in text or "enosys" in text + errno = getattr(exc, "errno", None) + return ( + errno in (13, 5) + or "access denied" in text + or "permission" in text + or "busy" in text + or "resource busy" in text + ) + + +def _claim_failure_message(ifnum: int, exc: BaseException) -> str: + base = f"USB interface {ifnum} 점유 실패: {exc}" + if not _IS_WIN: + return base + if _is_access_denied(exc): + return ( + f"{base}\n\n" + "Windows가 Interface 5를 다른 드라이버(공식 Wormhole 등)로 붙잡고 있습니다.\n" + "아래를 순서대로 하세요:\n" + "1) 작업 관리자에서 Wormhole / SmartDataLink / j5create 관련 프로그램 모두 종료\n" + "2) Zadig(https://zadig.akeo.ie/) 실행 (관리자)\n" + "3) Options → List All Devices\n" + "4) 목록에서 'Smart Data Link (Interface 5)' 또는 InstanceId에 MI_05 가 있는 항목 선택\n" + "5) Driver 를 WinUSB 로 바꾼 뒤 Replace Driver\n" + "6) 케이블 뽑았다가 다시 꽂고 JUC500 재실행 → 연결\n" + "장치 관리자에서 Interface 5가 'WinUSB' / 'libusb-winusb' 인지도 확인하세요." + ) + return ( + f"{base}\n" + f"Zadig에서 Smart Data Link → Interface {ifnum} (MI_0{ifnum}) 에 WinUSB를 설치하세요." + ) @dataclass @@ -435,14 +465,7 @@ class Juc500Link: usb.util.claim_interface(self._dev, ifnum) except usb.core.USBError as exc: if required: - hint = "" - if _IS_WIN: - hint = ( - f"\nZadig에서 Smart Data Link → Interface {ifnum} (MI_0{ifnum}) 에 " - "WinUSB 를 설치하세요. 데이터 전송은 Interface 5가 필수입니다.\n" - "공식 j5create Wormhole 드라이버가 있으면 제거하고 다시 시도하세요." - ) - raise RuntimeError(f"USB interface {ifnum} 점유 실패: {exc}{hint}") from exc + raise RuntimeError(_claim_failure_message(ifnum, exc)) from exc return self._claimed.append(ifnum)